VB6: Einbinden einer C Dll Funktion mit Void Pointern in VB6

  • VB6

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von EPM.

    VB6: Einbinden einer C Dll Funktion mit Void Pointern in VB6

    Hallo,

    neben anderen Tätigkeiten, betreue ich seit einigen Jahren ein recht umfangreiches VB6 Projekt. Das ist auch der Grund warum es immer noch unter VB6 läuft. Bisher war ich immer nach Recherchen zurecht gekommen, wenn ich von alleine nicht weiter kam. Zu diesem Thema finde ich jedoch nicht ausreichend Informationen.

    Ich versuche seit Tagen heraus zu finden wie ich eine C dll einbinden bzw. deren Hauptfunktion mit Void Pointern aufrufen muss. Bisher stürzt bei jedem versuch die IDE ab sobald der Aufruf erfolgt. Testweise habe ich das ganze auch schon in VBA mit dem gleichen Ergebnis probiert. Excel stürzt ebenfalls ab. Die Hauptfunktion hat einige Void Pointer die auf Parameter Arrays zeigen als Ein- und Ausgabe. Die meisten Parameter sind als Double definiert. einzelne Pointer zeigen auf char Parameter.

    Neben der Hauptfunktion enthält die dll ein paar deutlich vereinfachte Testfunktionen die ich problemlos aufgerufen bekomme auch wenn ggf. die Syntax nicht perfekt ist. Die Testfunktionen haben jedoch keine Parameter auf die die Pointer zeigen wie es die Hauptfunktion tut. Diese Pointer laufen direkt in Rückgabewerte. Ich vermute das an meinem Aufruf bzw. der Deklaration immer noch etwas nicht in Ordnung ist. Leider habe ich bisher keine eindeutigen Informationen gefunden, wie mit Void Pointern unter VB6 richtig umzugehen ist. Manche sprechen über ByRef Pointer as Any in der Deklaration andere sagen es geht so wie ich es habe. Andere sprechen über InPtr bzw. InStr. Wobei mir auch hier der Einsatz nicht eindeutig klar geworden ist. Beides habe ich schon in verschieden Variationen getestet bin damit aber auch nicht weiter gekommen. Ich würde mir wünschen wenn mir jemand aufzeigen kann wie man die die Deklaration und den Aufruf von Funktionen mit Void Pointern in VB6 richtig macht.

    Dies ist der dll Header:

    C-Quellcode

    1. #ifndef _FUNC1_H
    2. #define _FUNC1_H
    3. #define GROUP_SIZE 512
    4. #ifndef UNIT_TEST
    5. #ifdef EXPORT_FCNS
    6. #define EXPORTED_FUNCTION __declspec(dllexport)
    7. #else
    8. #define EXPORTED_FUNCTION __declspec(dllimport)
    9. #endif
    10. #else
    11. #define EXPORTED_FUNCTION
    12. #endif
    13. /**
    14. * The DLL can operate in a variety of "modes" and on the basis of the input parameters it
    15. * creates a output file. The input/output parameters are arrays of void pointers.
    16. */
    17. EXPORTED_FUNCTION int _stdcall test1(void);
    18. EXPORTED_FUNCTION int _stdcall test2(char in);
    19. EXPORTED_FUNCTION int _stdcall test3(void *in);
    20. EXPORTED_FUNCTION int _stdcall test4(void *in, void *out);
    21. EXPORTED_FUNCTION int _stdcall mainF(unsigned char mode,
    22. void * group_in_1,
    23. void * group_in_2,
    24. void * group_in_3,
    25. void * group_in_4,
    26. void * group_in_5,
    27. void * group_in_6,
    28. void * group_in_7,
    29. void * group_in_8,
    30. void * group_in_9,
    31. void * group_in_10,
    32. void * group_in_11,
    33. void * group_in_12,
    34. void * group_in_13,
    35. void * group_in_14,
    36. void * group_in_15,
    37. void * group_in_16,
    38. void * group_out_1,
    39. void * group_out_2,
    40. void * group_out_3,
    41. void * group_out_4,
    42. void * group_out_5,
    43. void * group_out_6);
    44. #endif


    Dies ist ein Auszug aus der Variablen Referenz dieser DLL.


    Dies ist was ich zuletzt in VB /VBA versucht habe

    Visual Basic-Quellcode

    1. Option Explicit
    2. Private Declare Function mainF Lib "c:\temp\dmls.dll" (ByVal Mode As Byte, _
    3. ByRef GI1 As Double, ByRef GI2 As Double, _
    4. ByRef GI3 As Double, ByRef GI4 As Double, _
    5. ByRef GI5 As Double, ByRef GI6 As Double, _
    6. ByRef GI7 As Double, ByRef GI8 As Double, _
    7. ByRef GI9 As Double, ByRef GI10 As Double, _
    8. ByRef GI11 As Byte, ByRef GI12 As Byte, _
    9. ByRef GI13 As Double, ByRef GI14 As Double, _
    10. ByRef GI15 As Double, ByRef GI16 As Double, _
    11. ByRef GO1 As Double, ByRef GO2 As Double, _
    12. ByRef GO3 As Byte, ByRef GO4 As Double, _
    13. ByRef GO5 As Double, ByRef GO6 As Double) As Long
    14. Private Sub CallDLL()
    15. Dim Success As Long ' VB Long is equal to C Integer32
    16. Dim C_Mode As Byte
    17. Dim GroupI1(511) As Double 'Arrays for pointers
    18. Dim GroupI2(511) As Double
    19. Dim GroupI3(511) As Double
    20. Dim GroupI4(511) As Double
    21. Dim GroupI5(511) As Double
    22. Dim GroupI6(511) As Double
    23. Dim GroupI7(511) As Double
    24. Dim GroupI8(511) As Double
    25. Dim GroupI9(511) As Double
    26. Dim GroupI10(511) As Double
    27. Dim GroupI11(127) As Byte 'Data In ist 128 Bytes groß, mit 511 auch schon probiert
    28. Dim GroupI12(6) As Byte 'mein Pfad hat nur 7 Zeichen, mit 511 auch schon probiert
    29. Dim GroupI13(511) As Double
    30. Dim GroupI14(511) As Double
    31. Dim GroupI15(511) As Double
    32. Dim GroupI16(511) As Double
    33. Dim GroupO1(511) As Double
    34. Dim GroupO2(511) As Double
    35. Dim GroupO3(127) As Byte
    36. Dim GroupO4(511) As Double
    37. Dim GroupO5(511) As Double
    38. Dim GroupO6(511) As Double
    39. Dim i As Integer 'count variable
    40. On Error GoTo error
    41. C_Mode = 1
    42. For i = 0 To 511
    43. GroupI1(i) = i
    44. GroupI2(i) = i
    45. GroupI3(i) = i
    46. GroupI4(i) = i
    47. GroupI5(i) = i
    48. GroupI6(i) = i
    49. GroupI7(i) = i
    50. GroupI8(i) = i
    51. GroupI9(i) = i
    52. GroupI10(i) = i
    53. GroupI13(i) = i
    54. GroupI14(i) = i
    55. GroupI15(i) = i
    56. GroupI16(i) = i
    57. Next
    58. GroupI12(0) = 99 'c ASCII Path
    59. GroupI12(1) = 58 ':
    60. GroupI12(2) = 92 '\
    61. GroupI12(3) = 116 't
    62. GroupI12(4) = 101 'e
    63. GroupI12(5) = 109 'm
    64. GroupI12(6) = 112 'p
    65. For i = 0 To 126 'Data In
    66. GroupI11(i) = 0
    67. Next
    68. GroupI11(127) = 255
    69. Success = mainF(C_Mode, _
    70. GroupI1(0), GroupI2(0), _
    71. GroupI3(0), GroupI4(0), _
    72. GroupI5(0), GroupI6(0), _
    73. GroupI7(0), GroupI8(0), _
    74. GroupI9(0), GroupI10(0), _
    75. GroupI11(0), GroupI12(0), _
    76. GroupI13(0), GroupI14(0), _
    77. GroupI15(0), GroupI16(0), _
    78. GroupO1(0), GroupO2(0), _
    79. GroupO3(0), GroupO4(0), _
    80. GroupO5(0), GroupO6(0))
    81. MsgBox (Success)
    82. Exit Sub
    83. error: MsgBox (Err.Number & " " & Err.Description)
    84. Resume Next
    85. End Sub






    Grüße

    EPM

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

    Hallo,
    ​das Problem ist Folgendes: Hast du eine 32 Bit Anwendung und übergibst einen Double, dann ist der Double 8 Byte groß und der Zeiger nur 4. Somit werden nur 4 Byte vom Stack gepopt -> Stackimbalance. Haste eine 64 Bit Anwendung, müsste das eigentliche Aufrufen sogar klappen da beide 8 Byte groß sind. Beim Abrufen des Speichers kommt allerdings Quatsch raus, oder es knallt. Du übergibst nämlich nicht die Adresse des Doubles sondern den Wert bzw. diese 8 Bytes die für Mantisse & Exponent sind. Leider habe ich keine Ahnung von VB6, aber im Grunde musst du nur 2 Dinge machen: Deklariere die Parameter als Zeiger (in .Net gibt's da IntPtr) und dann musst du dir noch die Adresse des Doubles besorgen. Ich hab andere Deklarationen gesehen, die Any benutzen, das sieht mir aus wie ein simpler Zeiger. ByRef ist aber Quatsch. Probierma einfach As Any zu deklarieren und dann packste deine Doubles rein.
    Hi,
    Danke für die Rückmeldung. As Any hatte ich schon versucht. Ich habe es auch schon mit VarPtr beim Funktionsaufruf probiert allerdings ebenfalls ohne Erfolg. An verschiedenen Stellen wird gesagt, das es prinzipiell funktionieren soll Pointer zu übergeben, allerdings fehlt mir da eine konkrete Beschreibung wie die Syntax für Declare und den Funktionsaufruf aussehen soll bzw. welche "No goes" es dabei gibt. Wie z.B. der Einwand von dir mit der Speichergröße vom Pointer und den Variablen. Ich habe auch schon an einen Wrapper gedacht um das Problem zu umgehen in dem z.B. alle Pointer Felder in einzelne Variablen umgesetzt werden und ich diese dann direkt ohne Pointer nutzen zu müssen übergeben kann. Bin mir dabei nur nicht sicher ob ich das mit meinem Grundlag nwissen in einer anderen Sprache aufgesetzt bekomme.
    Hmm, passt die Prozessorarchitektur denn? Also Dll und dein Programm x86 oder beide x64. As Any sollte eigentlich hinhauen. Achte aber auf die Größe deiner Arrays. Entweder ist die dokumentiert und fix, oder du musst z.B. bei dem Pfad ein '\0' anhängen. Das ist eigentlich gängig. Ein möglicher Fehler könnte sein, dass dein Pfad "C:\temp\" durchiteretiert wird bis zu einem zufälligen '\0' im Speicher, oder bis in fremden Speicher (AccessViolation). Gibt es einen Grund warum du kein VB.NET benutzt? Vielleicht kannst du es ja einfach testweise nutzen bis die Kommunikation zur Dll klappt. Ansonsten wäre ein Wrapper eigentlich ganz easy:
    1. ​Mach dir eine C-Dll
    2. Linke zu deiner dmls.dll (musst dem Linker die dmls.lib geben und dem Compiler die Header)
    3. Dann nur noch ne Methode deklarieren, die die Typensicherheit herstellt.

    C-Quellcode

    1. ​__declspec(dllexport) int __stdcall main_f_double(unsigned char mode, double* gi1, double* gi2, double* gi3, double* gi4, double* gi5, double* gi6, double* gi7, double* gi8, double* gi9,
    2. double* gi10, char* gi11, char* gi12, double* gi13, double* gi14, double* gi15, double* gi16, double* go1, double* go2, char* go3, double* go4, double* go5, double* go6)
    3. {
    4. return mainF(mode, gi1, gi2, gi3, gi4, gi5, gi6, gi7, gi8, gi9, gi10, gi11, gi12, gi13, gi14, gi15, gi16, go1, go2, go3, go4, go5, go6);
    5. }

    ​Ansonsten musste mal deinen aktuellen Code zeigen mit Any und wie die Arrays übergeben werden. Gibt's überhaupt ne Fehlermeldung, oder stürzt die IDE nur ab?
    Es soll die 32Bit Version der dll sein. Bei der Arraygröße sollen 512 Elemente pro Pointer reserviert werden, es sind tatsächlich aber deutlich weniger belegt. Ist wohl als Platzhalter für Erweiterungen innerhalb der dll gedacht. Es wird beschrieben, das jeder Pointer nur soweit hoch läuft wie auch z.Z. Elemente vorhanden sind. Ich habe noch nicht versucht die Arraygrößen an die tatsächlich verwendete Anzahl anzupassen, weil ich irgendwo gelesen hatte, die Arrays dürften ruhig größer sein und ungenutzte Felder nach hinter heraus blieben einfach leer / ungenutzt.

    Das Projekt in dem diese dll eingebunden werden muss ist sehr umfangreich so dass das heben auf .NET ohne es zu Debuggen vermutlich schon Monate dauern würde. Wir waren schon oft kurz davor es anzugehen, allerdings kamen immer andere Prios dazwischen, so das ich immer noch gezwungen bin es in VB6 zu pflegen. Ich hatte vorhin auch schon daran gedacht es einfach mal testweise mit .net zu versuchen wenn ich hier nicht weiter komme. Dann würde ich darauf aufbauend irgend ein Workaround machen.

    Leider stürzt die IDE immer ab, egal was ich bisher versucht habe. VB6 SP5 läuft bei mir auf einem separaten XP PC. Da VBA und VB6 praktisch identisch sind versuche ich es auch in Excel. Excel stürzt ebenfalls ab.

    So sieht es mit Any und Pfad Terminierung aus. Der Rest bleibt wie oben. Hatte es wahlweise auch mit VarPtr probiert:

    Visual Basic-Quellcode

    1. Option Explicit
    2. Private Declare Function mainF Lib "c:\temp\dmls.dll" (ByVal CMode As Byte, _
    3. GI1 As Any, GI2 As Any, _
    4. GI3 As Any, GI4 As Any, _
    5. GI5 As Any, GI6 As Any, _
    6. GI7 As Any, GI8 As Any, _
    7. GI9 As Any, GI10 As Any, _
    8. GI11 As Any, GI12 As Any, _
    9. GI13 As Any, GI14 As Any, _
    10. GI15 As Any, GI16 As Any, _
    11. GO1 As Any, GO2 As Any, _
    12. GO3 As Any, GO4 As Any, _
    13. GO5 As Any, GO6 As Any) As Long
    14. 'Private Declare Function mainF Lib "c:\temp\dmls.dll" (ByVal CMode As Byte, _
    15. ' ByRef GI1 As Double, ByRef GI2 As Double, _
    16. ' ByRef GI3 As Double, ByRef GI4 As Double, _
    17. ' ByRef GI5 As Double, ByRef GI6 As Double, _
    18. ' ByRef GI7 As Double, ByRef GI8 As Double, _
    19. ' ByRef GI9 As Double, ByRef GI10 As Double, _
    20. ' ByRef GI11 As Byte, ByRef GI12 As Byte, _
    21. ' ByRef GI13 As Double, ByRef GI14 As Double, _
    22. ' ByRef GI15 As Double, ByRef GI16 As Double, _
    23. ' ByRef GO1 As Double, ByRef GO2 As Double, _
    24. ' ByRef GO3 As Byte, ByRef GO4 As Double, _
    25. ' ByRef GO5 As Double, ByRef GO6 As Double) As Long
    26. Dim C_Mode As Byte
    27. Dim GroupI1(511) As Double 'Arrays for pointers
    28. Dim GroupI2(511) As Double
    29. Dim GroupI3(511) As Double
    30. Dim GroupI4(511) As Double
    31. Dim GroupI5(511) As Double
    32. Dim GroupI6(511) As Double
    33. Dim GroupI7(511) As Double
    34. Dim GroupI8(511) As Double
    35. Dim GroupI9(511) As Double
    36. Dim GroupI10(511) As Double
    37. Dim GroupI11(127) As Byte
    38. Dim GroupI12(8) As Byte
    39. Dim GroupI13(511) As Double
    40. Dim GroupI14(511) As Double
    41. Dim GroupI15(511) As Double
    42. Dim GroupI16(511) As Double
    43. Dim GroupO1(511) As Double
    44. Dim GroupO2(511) As Double
    45. Dim GroupO3(127) As Byte
    46. Dim GroupO4(511) As Double
    47. Dim GroupO5(511) As Double
    48. Dim GroupO6(511) As Double
    49. GroupI12(0) = 99 'c 'Debug Path
    50. GroupI12(1) = 58 ':
    51. GroupI12(2) = 92 '\
    52. GroupI12(3) = 116 't
    53. GroupI12(4) = 101 'e
    54. GroupI12(5) = 109 'm
    55. GroupI12(6) = 112 'p
    56. GroupI12(7) = 92 '\
    57. GroupI12(8) = 0 '0
    58. 'Success = mainF(C_Mode, _
    59. ' GroupI1(0), VarPtr(GroupI2(0)), _
    60. ' VarPtr(GroupI3(0)), VarPtr(GroupI4(0)), _
    61. ' VarPtr(GroupI5(0)), VarPtr(GroupI6(0)), _
    62. ' VarPtr(GroupI7(0)), VarPtr(GroupI8(0)), _
    63. ' VarPtr(GroupI9(0)), VarPtr(GroupI10(0)), _
    64. ' VarPtr(GroupI11(0)), VarPtr(GroupI12(0)), _
    65. ' VarPtr(GroupI13(0)), VarPtr(GroupI14(0)), _
    66. ' VarPtr(GroupI15(0)), VarPtr(GroupI16(0)), _
    67. ' VarPtr(GroupO1(0)), VarPtr(GroupO2(0)), _
    68. ' VarPtr(GroupO3(0)), VarPtr(GroupO4(0)), _
    69. ' VarPtr(GroupO5(0)), VarPtr(GroupO6(0)))


    Die dll kann ich leider nicht online stellen damit es jemand anders mal versuchen kann.
    Moin,

    hab nun das Problem gefunden. Ich hatte mir leider anfangs nichts dabei gedacht. Die Headerdatei und das Parameter Referenz Dokument hatten unterschiedlich viele Pointer geführt. Ich hatte mich an die Header Datei gehalten weil die zusammen mit der dll. bereit gestellt war. Die scheint aber nicht upgedated worden zu sein.

    Auf jeden Fall schmiert die IDE nicht mehr ab sobald ich die richtige Anzahl an Pointern und wieder halbwegs die richtige Variablen Deklaration hatte.

    Der Code im Eingangs-Post ist praktisch richtig. Hab nur die Fehlenden Pointer ergänzt. Das Pfad Array lässt sich dann auch einfach als String übergeben.

    Trotzdem Danke für deine Hilfe :thumbsup: