DLLCall mit Pointer auf Struktur-Array

  • Ich möchte eine DLL-Funktion einbinden die einen Pointer auf ein Struktur-Array erwartet. (+max. Anzahl der Strukturen) . Für jedes gefundene Gerät wird eine Struktur ausgefüllt.
    Die Funktion gibt die Anzahl der ausgefüllten Strukturen nach dem Aufruf zurück.
    In C schaut das ca. so aus:

    Spoiler anzeigen

    Wenn ich die DLL nun in AutoIt benutzen will wie muss ich da jetzt mit "DllStructCreate" die Struktur erstellen?
    Muss ich da jetzt 5mal hintereinander die daten eintippen?

    Spoiler anzeigen
    [autoit]


    Local $tagDDA=""
    $tagDDA &= "STRUCT; ULONG snr1; CHAR name1[256]; int dhcp1; CHAR ip1[24]; char netmask1[24]; char gateway1[24]; int signature1; ENDSTRUCT"
    $tagDDA &= "STRUCT; ULONG snr2; CHAR name2[256]; int dhcp2; CHAR ip2[24]; char netmask2[24]; char gateway2[24]; int signature2; ENDSTRUCT"
    $tagDDA &= "STRUCT; ULONG snr3; CHAR name3[256]; int dhcp3; CHAR ip3[24]; char netmask3[24]; char gateway3[24]; int signature3; ENDSTRUCT"
    $tagDDA &= "STRUCT; ULONG snr4; CHAR name4[256]; int dhcp4; CHAR ip4[24]; char netmask4[24]; char gateway4[24]; int signature4; ENDSTRUCT"

    [/autoit] [autoit][/autoit] [autoit]

    Local $DDA_struct = DLLSTRUCTCREATE($tagDDA)

    [/autoit] [autoit][/autoit] [autoit]

    Local $aReturn = DllCall('ChipControl.dll', 'int', 'CCGetAllDevsInArray', 'struct*', $DDA_struct, 'int', 4)

    [/autoit]

    Da ich momentan nur ein Device habe das gefunden wird kann ich es nicht ausprobieren ob es so passt.
    Bzw. gibt es eine Möglichkeit das ich so a la DDA[1].snr darauf zugreifen kann?

    Bzw. noch eine andere Frage, bin nicht so fix mit den Pointern :S
    Sind die beiden Varianten beim DllCall gleichwertig?

    [autoit]


    Local $aReturn = DllCall('ChipControl.dll', 'int', 'CCGetAllDevsInArray', 'struct*', $DDA_struct, 'int', 4)
    bzw.
    Local $aReturn = DllCall('ChipControl.dll', 'int', 'CCGetAllDevsInArray', 'ptr', DllStructGetPtr($DDA_struct), 'int', 4)

    [/autoit][autoit][/autoit][autoit][/autoit]


    THX,
    Indi2Go

    Einmal editiert, zuletzt von Indi2Go (20. Dezember 2013 um 19:41)

  • Um Gottes willen! Nein,... Auf keinen Fall so xD
    Du musst die Struktur glücklicherweise nur ein einziges mal eintippen (glück gehabt ^^)
    Das sieht in etwa dann so aus:

    Spoiler anzeigen
    [autoit]

    Global $tagDDA = 'ULONG snr;' & _
    'CHAR name[256];' & _
    'int dhcp;' & _
    'CHAR ip[24];' & _
    'CHAR netmask[24];' & _
    'CHAR gateway[24];' & _
    'int signature'
    Global $tDDA = DllStructCreate($tagDDA)

    [/autoit]

    Wenn du nun 5 Strukturen brauchst, kannst du diese einfach kopieren.
    Ich schlage vor Du schreibst die Kopien in ein Array:

    Spoiler anzeigen
    [autoit]

    Global $atDDA[5], $i
    For $i = 0 To UBound($atDDA) -1
    $atDDA[$i] = $tDDA
    Next

    [/autoit]

    Den Zugriff auf die einzelnen Strukturelemente mit dem Punkt Operator ist seit der AutoIt Version 3.3.9.6 (Beta) möglich.
    In allen älteren Versionen nur mit den Funktionen „DllStructSetData“ sowie „DllStructGetData“.

    Mit Punkt Operator (Dafür den Präprozessor Befehl „#AutoIt3Wrapper_Version = B“ setzen):

    Spoiler anzeigen
    [autoit]

    $atDDa[0].snr = 1
    $atDDa[1].snr = 2
    $atDDa[2].snr = 3
    $atDDa[3].snr = 4
    $atDDa[4].snr = 5

    [/autoit]

    Mit den DLL-Funktionen:

    Spoiler anzeigen
    [autoit]

    DllStructSetData($atDDA[0], 'snr', 1)
    DllStructSetData($atDDA[1], 'snr', 2)
    DllStructSetData($atDDA[2], 'snr', 3)
    DllStructSetData($atDDA[3], 'snr', 4)
    DllStructSetData($atDDA[4], 'snr', 5)

    [/autoit]

    Danach nur noch die DLL Aufrufe :)

    Spoiler anzeigen
    [autoit]

    Global $aiRet[5], $avRet, $pStruct
    For $i = 0 To UBound($atDDA) -1
    $pStruct = DllStructGetPtr($atDDA[$i])
    $avRet = DllCall('ChipControl.dll', 'int', 'CCGetAllDevsInArray', 'ptr', $pStruct, 'int', 4)
    $aiRet[$i] = $avRet[0]
    Next

    [/autoit]

    Hier noch der ganze Code mit Ausgabe der Rückgabewerte:

    Spoiler anzeigen
    [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    Global $i, $atDDA[5], $pStruct, $avRet, $aiRet[5]
    Global $tagDDA = 'ULONG snr;' & _
    'CHAR name[256];' & _
    'int dhcp;' & _
    'CHAR ip[24];' & _
    'CHAR netmask[24];' & _
    'CHAR gateway[24];' & _
    'int signature'
    Global $tDDA = DllStructCreate($tagDDA)

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($atDDA) -1
    $atDDA[$i] = $tDDA
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    DllStructSetData($atDDA[0], 'snr', 1)
    DllStructSetData($atDDA[1], 'snr', 2)
    DllStructSetData($atDDA[2], 'snr', 3)
    DllStructSetData($atDDA[3], 'snr', 4)
    DllStructSetData($atDDA[4], 'snr', 5)

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($atDDA) -1
    $pStruct = DllStructGetPtr($atDDA[$i])
    $avRet = DllCall('ChipControl.dll', 'int', 'CCGetAllDevsInArray', 'ptr', $pStruct, 'int', 4)
    $aiRet[$i] = $avRet[0]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    _ArrayDisplay($aiRet)

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit]


    Zu deiner Frage:
    „ptr“ nimmt jeden Pointer (egal welcher Datentyp) an.
    „struct*“ dagegen nur Pointer von DLL Strukturen.
    Wenn ich mich nicht irre ist beides in deinem Fall verwendbar.

  • Hi,
    danke für die schnelle Antwort!

    Zitat

    Um Gottes willen! Nein,... Auf keinen Fall so xD
    Du musst die Struktur glücklicherweise nur ein einziges mal eintippen

    Darauf bin ich eh nicht scharf... ^^

    Aber ich bin mir in dem Fall nicht sicher, da das eine C-Struktur ist, wie das mit dem "alignment" läuft.
    Oder da die Struktur nur aus durch 8 teilbare Anzahl von char-Arrays besteht brauch ich mir da keine Gedanken machen?

    Bezüglich des Aufrufs.
    Das "Struktur-Array" wird von der DLL-Funktion gefüllt. Es wird der Pointer auf das erste (Struktur-)Element und die Größe der Elemente angegeben. Falls die Funktion mehr Geräte finden sollte wird diese Anzahl zurückgegeben, aber eben nur die max. Anzahl an Strukturelementen befüllt.
    Nach dem Aufrufen steht in dem Struktur-array die Daten der gefundenen Geräte.

    Wenn ich das ganze in einem Array, so wie Du angegebn hast, steht das so hintereinander im Speicher wie in 'C'? Damit wenn die (DLL-)Funktion mit dem Pointer weitergeht die nächste Struktur ausgefüllt wird?

    Ich stell mir das so vor (aber ob das funzt?):
    (ist mal nur graue theorie, da heute ja SO & ich das erst morgen ausprobieren kann...)

    Spoiler anzeigen
    [autoit]


    Global $i, $atDDA[5], $pStruct, $avRet, $ccSNR[5]
    Global $tagDDA = 'ULONG snr;' & _
    'CHAR name[256];' & _
    'int dhcp;' & _
    'CHAR ip[24];' & _
    'CHAR netmask[24];' & _
    'CHAR gateway[24];' & _
    'int signature'
    Global $tDDA = DllStructCreate($tagDDA)

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($atDDA) -1
    $atDDA[$i] = $tDDA
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    $pStruct = DllStructGetPtr($atDDA[0])
    $avRet = DllCall('ChipControl.dll', 'int', 'CCGetAllDevsInArray', 'ptr', $pStruct, 'int', UBound($atDDA))

    if $avRet[0] > UBound($atDDA) then
    MsgBox(64,"CC-Scan", $avRet[0] & " Geräte gefunden!")
    EndIf
    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To $avRet[0]
    $ccSNR[$i] = DllStructGetData($tDDA[$i],'snr') ;Hier bin ich mir sehr unsicher, ob das so geht!
    Next

    [/autoit] [autoit][/autoit] [autoit][/autoit]

    Einmal editiert, zuletzt von Indi2Go (15. Dezember 2013 um 10:49)

  • Ich befürchte, dass Makes Idee nicht funktionieren wird.

    Erstens, benutzt du nur eine einzige Struktur. Du veränderst einfach von Zeile 25 bis 29 immer den selben Wert. ^^

    Spoiler anzeigen
    [autoit]


    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    Global $i, $atDDA[5], $pStruct, $avRet, $aiRet[5]
    Global $tagDDA = 'ULONG snr;' & _
    'CHAR name[256];' & _
    'int dhcp;' & _
    'CHAR ip[24];' & _
    'CHAR netmask[24];' & _
    'CHAR gateway[24];' & _
    'int signature'
    Global $tDDA = DllStructCreate($tagDDA)

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($atDDA) -1
    $atDDA[$i] = $tDDA
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    DllStructSetData($atDDA[0], 'snr', 1)
    DllStructSetData($atDDA[1], 'snr', 2)
    DllStructSetData($atDDA[2], 'snr', 3)
    DllStructSetData($atDDA[3], 'snr', 4)
    DllStructSetData($atDDA[4], 'snr', 5)

    [/autoit] [autoit][/autoit] [autoit]

    ; Cut

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite(DllStructGetData($atDDA[0], "snr") & @CRLF)

    [/autoit]


    Das DllStructCreate muss in die Schleife in Zeile 19 bis 21. ;)

    Außerdem...

    Du benutzt ein AutoIt-Array. Das Ergebnis des Aufrufes wird ja in einem Array of Struct gespeichert. Ich glaube kaum, dass so ein Aufruf funktioniert. Du müsstest das Array in einer Struktur erstellen.
    Also ein Array of Pointer mit den Pointern zu den DDA-Strukturen... Ich denke, so wird die DLL das handhaben? ^^

    Spoiler anzeigen
    [autoit]


    $iStructs = 4 ;Anzahl der Strukturen, im Beispiel 4

    [/autoit] [autoit][/autoit] [autoit]

    Local $atDDA[$iStructs]
    $tagDDA = "ulong snr; char name[256]; int dhcp; char ip[24]; char netmask[24]; char gateway[24]; int signature"
    $tagArrayOfPtr = "ptr [" & $iStructs & "];"

    [/autoit] [autoit][/autoit] [autoit]

    $tArrayOfDDA = DllStructCreate($tagArrayOfPtr)

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($atDDA) - 1
    $atDDA[$i] = DllStructCreate($tagDDA)
    DllStructSetData($tArrayOfDDA, 1, DllStructGetPtr($atDDA[$i]), $i + 1)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ;Debugging
    ;~ For $i = 1 To UBound($atDDA)
    ;~ ConsoleWrite("Pointer #" & $i & ": " & DllStructGetData($tArrayOfDDA, 1, $i) & @CRLF)
    ;~ Next
    ;~ ConsoleWrite("Pointer to Array: " & DllStructGetPtr($tArrayOfDDA) & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    DllCall("ChipControl.dll", "int", "CCGetAllDevsInArray", "ptr", DllStructGetPtr($tArrayOfDDA), "int", $iStructs)

    [/autoit] [autoit][/autoit] [autoit]

    ;Ausgabe:
    For $i = 0 To UBound($atDDA) -1
    ConsoleWrite("SNR => " & DllStructGetData($atDDA[$i], "snr") & "; ")
    ConsoleWrite("Name => " & DllStructGetData($atDDA[$i], "name") & "; ")
    ConsoleWrite("DHCP => " & DllStructGetData($atDDA[$i], "dhcp") & "; ")
    ConsoleWrite("IP => " & DllStructGetData($atDDA[$i], "ip") & "; ")
    ConsoleWrite("Netzmaske => " & DllStructGetData($atDDA[$i], "netmask") & "; ")
    ConsoleWrite("Gateway => " & DllStructGetData($atDDA[$i], "gateway") & "; ")
    ConsoleWrite("Signatur => " & DllStructGetData($atDDA[$i], "signature") & "; ")
    ConsoleWrite(@CRLF)
    Next

    [/autoit]

    Und nochmal zu ptr und struct*... Wie Make schon gesagt hat, ist die zweite Möglichkeit für Zeiger auf Strukturen gedacht. Das normale ptr dagegen ist - nach meinen Informationen - intern ähnlich wie ein void* in C. ^^

    Gruß

  • Zitat

    Außerdem...

    Du benutzt ein AutoIt-Array. Das Ergebnis des Aufrufes wird ja in einem
    Array of Struct gespeichert. Ich glaube kaum, dass so ein Aufruf
    funktioniert. Du müsstest das Array in einer Struktur erstellen.

    Also ein Array of Pointer mit den Pointern zu den DDA-Strukturen... Ich denke, so wird die DLL das handhaben? ^^

    Ja da liegt des Pudels Kern ;)
    Es wird eben IMHO ein Array von Strukturen erwartet & nicht ein Array von Pointern.

    Der Prototyp lautet: (siehe auch mein C-Beispiel ganz am Anang im ersten Post)

    Code
    int USERINT_FUNC CCGetAllDevsInArray(DevDataRecord * dda, int maxDevices)

    Deshalb dacht ich mir ich schreib das ganze mehrmals hintreinander & hoffe das das "C-struct" allignment mir nicht dreinpfuscht. Bzw. habe ich deshalb auch die 'STRUCT' Anweisung im DLLCreate drinnen.
    In den bis jetzt vorgebrachten Bsp. fehlt die STRUCT Anweisung immer braucht man das eh nicht?

    In der Hilfe zu DLLCreate steht:
    To use nested structures inside a structure you must re-define the nested structure. For example, a structure containing 2 POINT structures ("long;long") would be declared as "long;long;long;long".

    Deshalb dacht ich mir ich muss das Array of struct auch so auflösen das ich alles hintereinander schreibe.
    Aber falls es anders auch gehen sollte, bin ich für Vorschläge offen.

    Blöd ist nur das ich momentan(heute gar nicht, erst morgen wieder) nur ein Gerät am Bus habe & so das finden von mehreren Geräten nicht testen kann.

  • Tatsache, jetzt weiß ich, was gemeint war...
    Das C-Beispiel habe ich nur überflogen, und gedacht, dass ein Array of Pointer erwartet würde.

    Scheinbar müsstest du wirklich die gesamten Strukturdeklarationen hintereinander schreiben. Obwohl sich das auch vereinfachen lässt:

    [autoit]


    $tagStruct = "long x#; long y#;"
    $tArray = _DllStruct_CreateArray($tagStruct, 5)
    DllStructSetData($tArray, "x1", 42)
    ConsoleWrite(DllStructGetData($tArray, "x1") & @CRLF)

    [/autoit][autoit][/autoit][autoit]

    Func _DllStruct_CreateArray($tagStruct, $iBound, $sCounterString = "#")
    Local $tagMulti = ""

    [/autoit][autoit][/autoit][autoit]

    For $i = 0 To $iBound - 1
    $tagMulti &= StringReplace($tagStruct, $sCounterString, $i)
    Next

    [/autoit][autoit][/autoit][autoit]

    Return DllStructCreate($tagMulti)
    EndFunc

    [/autoit]

    Das müsste ja dann relativ einfach gehen. ^^

    Gruß

  • Doch klar,...
    Chess seine Variante müsste so funktionieren:

    [autoit]

    Global Const $tagDDA = 'ULONG snr; CHAR name[256]; INT dhcp; CHAR netmask[24]; CHAR gatewar[24]; INT signature'
    Global Const $iIndex = 4

    [/autoit][autoit][/autoit][autoit]

    Global $tStruct = DllStructCreate('STRUCT pointer[' & $iIndex & ']')
    Global $atStruct[$iIndex]
    Global $i, $pStruct, $avRet

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    For $i = 0 To $iIndex -1
    $atStruct[$i] = DllStructCreate($tagDDA)
    $pStruct = DllStructGetPtr($atStruct[$i])

    [/autoit][autoit][/autoit][autoit]

    DllStructSetData($tStruct, 'pointer', $pStruct, $i +1) ;// Struktur füllen
    Next

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    $pStruct = DllStructGetPtr($tStruct)

    [/autoit][autoit][/autoit][autoit]

    $avRet = DllCall('ChipControl.dll', 'int', 'CCGetAllDevsInArray', _
    'ptr*', $pStruct, _ ;// Das hier ist jetzt Tricky :)
    ;// Der Übergebene Pointer zeigt zwar auf eine Struktur,
    ;// aber du willst ja die Strukturen die sich hinter den Pointer verstecken.
    ;// Demnach musst du angeben dass der Pointer auf weitere Pointer verweist.
    ;// Also: ptr* ^^
    'int', $iIndex)

    [/autoit]

    3 Mal editiert, zuletzt von Yjuq (16. Dezember 2013 um 02:48)

  • Hab das ganze zum laufen bekommen. Ein fehlende ":cdecl" beim DLLCall lies das Skript noch abstürzen, aber jetzt funzt es einwandfrei.

    chesstiger
    Danke Deine Lösung hat mir einigen copy&paste&replace aufwand erspart

    @make
    Deine Lösung ist wahrlich trickey . Nur erwartet die DLL- Funktion eben einen Pointer auf ein Array der Sruktur. (& nicht ein Array von Pointer die auf die Struktur zeigen)

    Jedenfalls nochmals vielen Dank euch beiden. :thumbup: