Probleme mit der COM in VB.NET

  • VB.NET

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von Borgel.

    Probleme mit der COM in VB.NET

    Guten Tag,

    ich habe eine Problematik die ich nicht gelöst bekomme. Ich verwende ein Aktiv X Steuerelement dessen Bibliothek in C++ geschrieben ist. Durch die COM ist es kein Thema diese in VB.Net einzusetzen.

    Jetzt kommt aber der Haken. Ich benötige aus der Bibliothek eine Funktion, die mir das momentane Bild aus einem Livestream zurückgibt. Diese Funktion gibt mir aber leider nur die Adresse des Zeigers wieder. Da es aber in VB.Net keine Zeiger gibt (ich kenne nur das ByRef), weiß ich jetzt nicht, wie ich einem Graphics oder Bitmap Objekt die Information zukommen lassen kann.

    Ich würde mich sehr darüber freuen wenn Jemand einen Lösungsansatz hat.
    Hi,

    es kommt darauf an, welchen Datentyp die Funktion zurückgibt. Man kann mit Methoden aus System.Runtime.InteropServices.Marshal Daten aus dem nicht verwalteten Speicher lesen. Dafür musst du aber den Typ angeben. Ich nehme mal stark an, dass das Bild in Form eines Zeigers auf ein Byte-Array zurückkommt. Dieser Zeiger kommt dann als IntPtr an. Den kannst du an Copy(IntPtr, Byte(), Int32, Int32) übergeben. Das Byte-Array muss in der richtigen Länge vorinitialisiert sein, wofür die Länge bekannt sein muss. Die Länge und der Startindex müssen zusätzlich an die Funktion übergeben werden. Das beschriebene Byte-Array kopierst du dann in einen System.IO.MemoryStream, welchen du wiederum an ein neues Bitmap-Objekt übergeben kannst. Der Umweg über den MemoryStream ist nötig, weil der Bitmap-Klassenkonstruktor in keiner seiner Überladungen ein Byte-Array akzeptiert, wohl aber einen Stream. Wenn du mir die COM-Library zukommen lässt (oder deren Namen), kann ich dir auch ein kleines Beispiel schreiben. Ansonsten artet das Ganze nur zur wilden Raterei aus, was wohl funktionieren könnte.

    Die anfängliche Frage steht also noch im Raum: Welchen Zeigertyp gibt die Funktion zurück?
    Gruß
    hal2000
    Ich habe diese Variante ausprobiert. Führte aber zu einem "Allgemeiner Fehler in GDI+".

    VB.NET-Quellcode

    1. Sub Bild()
    2. Dim XAchse As Integer
    3. Dim YAchse As Integer
    4. Dim BildLaenge As IntPtr
    5. Dim BildZeiger As IntPtr
    6. AxMxPEG_ActiveX1.GetAktBitMap(BildZeiger, BildLaenge, XAchse, YAchse)
    7. PictureBox1.Image = Image.FromHbitmap(BildZeiger)
    8. End Sub


    Die Funktion sieht so aus .GetAktBitMap(ByRef phOutData as Integer, ByRef pOutDataLen As Integer, ByRef pOutResX as Integer, ByRef pOutResY As Integer) As Integer

    Das hier ist die Beschreibung des MxPEG ActiveX

    Name: GetAktBitMap
    ID: 53
    Return: long
    Parameters: OLE_HANDLE* phOutData, long* pOutDataLen, long* pOutResX, long* pOutResY
    Description: The ActiveX control will alloc a global buffer and copy its internal bitmap buffer into it. The handle of type HGLOBAL will be written to the variable that phOutData points to. The size will be written to the variable that pOutDataLen points to. pOutResX and pOutResY are pointers. If not equal 0 the current size will be stored in the positions they point to. The Application has to free the global buffer! Search for GlobalAlloc GlobalLock GlobalUnlock GlobalFree for more Information


    Die SDK ist von der Firma Mobotix, ich hab sie dir mal per PN zugeschickt.
    Danke für die Hilfe.
    Ich habe das jetzt versucht. Jedoch gelingt es mir nicht. An der Zeile 24 kommt immer wieder "Ungültiger Parameter".

    VB.NET-Quellcode

    1. Sub Bild()
    2. Dim XAchse As Integer
    3. Dim YAchse As Integer
    4. Dim BildLaenge As Integer
    5. Dim BildZeiger As IntPtr
    6. Dim images() As Byte
    7. Dim img As Image
    8. AxMxPEG_ActiveX1.GetAktBitMap(BildZeiger, BildLaenge, XAchse, YAchse)
    9. ReDim images(BildLaenge)
    10. Copy(BildZeiger, images, 0, BildLaenge)
    11. Byte2Image(img, images)
    12. End Sub
    13. Public Sub Byte2Image(ByRef NewImage As Image, ByVal ByteArr() As Byte)
    14. Dim ImageStream As MemoryStream
    15. Try
    16. ImageStream = New MemoryStream(ByteArr)
    17. NewImage = Image.FromStream(ImageStream)
    18. Catch ex As Exception
    19. End Try
    20. End Sub
    Ein erster Test vorweg:

    Benutze die Überladung Image.FromStream(Stream, Boolean) mit dem zweiten Parameter = True. Daher weißt du, ob dein Vorhaben nicht nur an eventuell eingebetteten Farbinformationen im Stream scheitert und deswegen keine gültige Bitmap darin erkannt wird.

    Wenn das nicht funktioniert:

    - Laut Dokumentation sind alle Parameter von GetAktBitMap() Pointer. Deine Deklarationen müsstest du dementsprechend anpassen:

    VB.NET-Quellcode

    1. Dim phOutData, pLength, pXres, pYres As IntPtr
    2. Dim FnResult As Integer
    3. FnResult = GetAktBitMap(phOutData, pLength, pXres, pYres)
    4. 'weiter unten beschrieben:
    5. Dim bmp As Bitmap = GetBitmap()

    Die Namensgebung sagt auch einiges aus (am Beispiel phOutData):
    p - Pointer (to a)
    h - Handle
    Out - Output
    Data - Daten
    Der Parameter ist also eine Ausgabe, die einen Pointer auf ein Handle für Daten liefert.

    Nun hast du Zeiger auf die Informationen, die du zum Auslesen der Daten benötigst.
    Für Copy(IntPtr, Byte(), Int, Int) brauchst du die Länge und den Offset sowie ein vorinitialisiertes Array.
    Also liest du diese Informationen aus (und gibst im selben Schritt gleich die Bitmap zurück):

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Imports System.Drawing
    3. Function GetBitmap() As Bitmap
    4. Dim intLen As Int32 = Marshal.ReadInt32(pLength) 'Länge auslesen
    5. Dim bytData(intLen) As Byte 'Array initialisieren
    6. Marshal.Copy(phOutData, bytData, 0, intLen) 'Daten auslesen
    7. Return CType(Image.FromStream(bytData), Bitmap) 'Bild erzeugen
    8. End Sub

    Du hast folgende Fehler gemacht:
    - Ein IntPtr (=Pointer) ist nicht dasselbe wie ein ByRef-Parameter
    - Pointer werden als "ByVal" übergeben, denn sonst übergibst du nicht den Zeiger, sondern einen "VB-Zeiger" (den es nicht wirkich gibt - er heißt in VB 'Referenz') auf den Zeiger!
    - Du hast den Pointer, der nur eine Speicheradresse ist, als Längenangabe in Copy() benutzt - damit hast du wahrscheinlich viel zu viel kopiert. Ein Beispiel:
    Gegeben sei ein IntPtr mit dem Wert 0xFFE2 - dieser Wert beschreibt eine Speicheradresse. In dieser Speicherzelle steht der Wert 534, der die Länge der (Nutz-)Daten beschreibt. Verwendest du nun 0xFFE2 direkt als Längenparameter, kopierst du nicht die gewollten 534 Byte, sondern umgerechnet 65506 Byte, denn 0xFFE2 entspricht 65506. Das sollte die Problematik verdeutlichen.

    Ein Anmerkung zu Schluss:
    Es kann sein, dass diese Methode wieder nicht funktioniert. Da ich gerade nicht zu Hause bin (und damit nur begrenzte Möglichkeiten habe), kann ich momentan nicht viel mehr tun. Morgen Abend bin ich wieder zu Hause und kann dort, wenn nötig, nach weiteren Lösungsansätzen suchen. Ich bitte um Rückmeldung.
    Gruß
    hal2000

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „hal2000“ ()

    Ich habe die Überladung Image.FromStream(Stream, Boolean) eingebunden, sie hat aber zum gleichen Ergebnis geführt, es ist der Fehler Ungültiger Parameter gekommen.

    Jetzt eine Feststellung von mir.

    Die Funktion ist so beschrieben:
    Public Overridable Function GetAktBitMap(ByRef phOutData As Integer, ByRef pOutDataLen As Integer, ByRef pOutResX As Integer, ByRef pOutResY As Integer) As Integer

    Richtig wäre aber gewesen:
    Public Overridable Function GetAktBitMap(ByRef phOutData As IntPtr, ByRef pOutDataLen As Integer, ByRef pOutResX As Integer, ByRef pOutResY As Integer) As Integer

    Mit folgender Begründung. Durch phOutData wird eine Adresse übergeben, dies kann man daran erkennen, dass sich diese bei einer neuen Kompilierung ändert. pOutDataLen, pOutResX und pOutResY enthalten jedoch statische Werte. Diese können auch nachvollzogen werden. pOutDataLen enthält den Wert 4915200 dieser bildet sich wie folgt pOutDataLen = pOutResX * pOutResY * 4 4915200 = 960 * 1280 * 4. Dabei steht die vier für die drei Grundfarben (RGB) plus einen Index in dem immer 0 steht.

    In die Variable FnResult wurde bis jetzt immer nur eine 0 reingeschrieben.

    Meine Vermutung ist das entweder am Anfang ein Bitmap-Header fehlt oder die 0 aus dem Byte-Array raus muss.
    Hi...

    Borgel schrieb:

    Meine Vermutung ist das entweder am Anfang ein Bitmap-Header fehlt oder die 0 aus dem Byte-Array raus muss.
    Das kann durchaus sein. Entferne mal testweise die führende 0 aus dem Array.
    Ich bin außerdem jetzt wieder zu Hause und werde mich damit bald beschäftigen.

    EDIT: Ich habe die AX-Komponente mal eingebunden - die Funktion kann ich allerdings nicht ausführen (verständlich, da ich ja keine Mobotix-Cam angeschlossen habe und es somit keine Datenquelle gibt).

    Ich kann dir also leider nur aus der Ferne helfen (oder du kennst eine Methode, mit der man der AX-Lib ein Bild unterjubeln kann).
    Gruß
    hal2000

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „hal2000“ ()

    Ich habe es nun geschafft das Bild aus dem Arbeitsspeicher zu holen. Jetzt habe ich nur das Problem das es total verzert dargestellt wird.

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices.Marshal
    2. Imports System.Drawing.Imaging
    3. Imports System.IO
    4. Public Class Form1
    5. Sub Bild()
    6. Dim index As Integer = 0
    7. Dim XAchse As Integer
    8. Dim YAchse As Integer
    9. Dim BildLaenge As Integer
    10. Dim BildZeiger As IntPtr
    11. Dim imageorg() As Byte
    12. Dim image(3686454) As Byte
    13. Dim ImageStream As MemoryStream
    14. 'Holt die relevanten Daten zu dem Bild im Arbeitsspeicher
    15. AxMxPEG_ActiveX1.GetAktBitMap(BildZeiger, BildLaenge, XAchse, YAchse)
    16. ReDim imageorg(BildLaenge)
    17. 'Kopiert das Bild aus dem Arbeitsspeicher in ein Byte-Array
    18. Copy(BildZeiger, imageorg, 0, BildLaenge)
    19. 'Hier stelle ich den BMP Header zusammen
    20. image(0) = 66
    21. image(1) = 77
    22. image(2) = 54
    23. image(3) = 64
    24. image(4) = 56
    25. image(5) = 0
    26. image(6) = 0
    27. image(7) = 0
    28. image(8) = 0
    29. image(9) = 0
    30. image(10) = 54
    31. image(11) = 0
    32. image(12) = 0
    33. image(13) = 0
    34. image(14) = 40
    35. image(15) = 0
    36. image(16) = 0
    37. image(17) = 0
    38. image(18) = 0
    39. image(19) = 5
    40. image(20) = 0
    41. image(21) = 0
    42. image(22) = 192
    43. image(23) = 3
    44. image(24) = 0
    45. image(25) = 0
    46. image(26) = 1
    47. image(27) = 0
    48. image(28) = 24
    49. image(29) = 0
    50. image(30) = 0
    51. image(31) = 0
    52. image(32) = 0
    53. image(33) = 0
    54. image(34) = 0
    55. image(35) = 0
    56. image(36) = 0
    57. image(37) = 0
    58. image(38) = 196
    59. image(39) = 14
    60. image(40) = 0
    61. image(41) = 0
    62. image(42) = 196
    63. image(43) = 14
    64. image(44) = 0
    65. image(45) = 0
    66. image(46) = 0
    67. image(47) = 0
    68. image(48) = 0
    69. image(49) = 0
    70. image(50) = 0
    71. image(51) = 0
    72. image(52) = 0
    73. image(53) = 0
    74. index = 54
    75. 'Hier werden die RGB Farben extrahiert
    76. For i As Integer = 0 To 4915200
    77. If imageorg(i) <> 0 Then
    78. image(index) = imageorg(i)
    79. index += 1
    80. End If
    81. Next
    82. 'Das Bild wird erzeugt
    83. ImageStream = New MemoryStream(image)
    84. PictureBox1.Image = Bitmap.FromStream(ImageStream)
    85. End Sub
    86. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    87. Bild()
    88. End Sub
    89. End Class

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Borgel“ ()

    Sooo...

    Jetzt bin ich wieder mal zu Hause (deswegen kommt die Antwort erst jetzt). Dein Problem ist, dass das Bild mit dem MxPEG-Codec komprimiert ist und VB das Ganze wie eine Bitmap behandelt. Dabei kommt zwar ein "Bild", darin gibts aber nur Pixelsalat. Auf der Mobotix-HP gibts noch eine C++ - Bibliothek , die mit "Decoder" beschrieben ist. Diese Bibliothek brauchst du, um das Bild in eine (für Windows lesbare) Bitmap zu konvertieren. Zitat: "Such a frame is no full JPEG" - gemeint ist hier ein MxPEG-Video-Frame. Windows kann das Bild trotzdem rudimentär interpretieren, denn: "It contains a minimal JPEG header and marker set."

    Nun zur Sache: Lade dir die mxgconv_win32.exe herunter - das ist die fertig kompilierte Bibliothek. Gib in einer Konsole mxgconv_win32.exe --help ein, um die Syntax zu erfahren. Der Konverter scheint automatisch zu arbeiten, d.h. alles, was du in StdIn schreibst, kommt sofort im eingestellten Format in StdOut heraus.
    Starte die .exe als eigenen Prozess und leite StdIn und StdOut um. Das Byte-Array mit den Daten aus der ActiveX-Library kopierst du nun nicht in den MemoryStream, sondern schreibst sie in StdIn des erstellten Prozesses. Den MemoryStream verbindest du mit StdOut. Übergib den Stream danach wieder an ein Bitmap-Objekt im JPEG- oder RGB- (RAW-) Modus. So solltest du ein vernünftiges Bild bekommen.

    Ich würde das Ganze auch gerne testen. Kannst du mir das (nicht-Bitmappisierte "imageorg" :) ) Byte-Array aus der Ax-Lib zukommen lassen? (Am besten mit BinaryFormatter serialisieren und in eine Datei schreiben).

    Gruß
    hal2000
    Gruß
    hal2000
    Ich hab das Problem nun komplett gelöst. Man muss den Header nur richtig einstellen.

    Ich hatte ihn hier vorher auf 24 Bit stehen.

    VB.NET-Quellcode

    1. image(28) = 24


    Die Lösung war, das man es auf 32 Bit stellen musste.

    VB.NET-Quellcode

    1. image(28) = 32


    Leider verschlingen die paar Zeilen sehr viel Ressourcen, das ist unglaublich. Mein Q9450 ist mit allen vier Kernen bei 40% Auslastung.