DllStruct Speichern

  • Moin,

    Das Problem das hier behoben werden soll ist folgendes: AutoIt räumt auf.
    Aufräumen ansich ist nicht verkehrt und bewahrt vor unnötiger Schreibarbeit.

    Das Array im Array dürfte jedem ein Begriff sein.
    Leider funktionniert Struct im Struct bei AutoIt nicht, warum ?

    Ganz einfach: Verliert AutoIt die direkte zuweisung der Struct über eine Variable wird die Struct gelöscht.
    Da hilft es auch nicht, wenn man den Pointer sowie die Struktur hinterlegt hat.

    Abhilfe soll dieses Skript schaffen. (Es befindet sich nicht in der "fertigen" Fassung, sondern in einer Debugversion mit eingebautem Test)
    Sofern das Skript funktioniert kann ich es auch ins UDF Schema verfrachten, sodass es bequem eingesetzt werden kann.

    "Funktionen und Sonstiges"
    [autoit]

    _SaveStruct($vStruct)
    ;Schützt eine DllStruct durch Zwischenlagerung vor dem bösen AutoIt Aufräumkommando.
    _DeleteStruct($iPointer)
    ;Löscht gezielt eine DllStruct aus dem Lager.
    $__Save_Intervall
    ;Zeigt an wie klein- oder großschrittig das Speicherarray erweitert oder verkürzt werden soll.
    ;bei sehr wenigen Structs ist kleinschrittiges vorgehen sinnvoll (z.B. 5)
    ;Bei sehr vielen Structs müsste somit sehr oft erweitert werden. Bei einem grßen Intervall (z.B. 50) braucht man hier aber nur eine Hand voll.
    ;Das Intervall darf beliebig im Skript an die Bedürfnisse angepasst werden.

    [/autoit]


    Ich bitte darum das Skript anzutesten und auf Fehler zu untersuchen.
    Wenn alles läuft wird es umgemodelt.

    Durch Auskommentieren von Zeile 91 sieht man was passiert, wenn eine Struct verloren geht, aber der Pointer noch vorhanden ist.

    "Hier steckt ein Skript"
    [autoit]


    #include <Array.au3>

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

    Global $__Save_Array[1], $__Save_Anzahl = 0, $__Save_Intervall = 5

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

    Global $vTest, $aPTR[16], $iTmp

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

    For $i = 0 To 15 Step 1 ; 16 Structpointer ergammeln und versuchen die Daten zu lesen.
    $aPTR[$i] = _ptr()
    $vTest = DllStructCreate('int[2]',$aPTR[$i])
    ConsoleWrite('Structinhalt: ' & DllStructGetData($vTest, 1, 1) & @CRLF)
    Next

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

    For $i = 0 To 15 Step 1 ; Alle 16 Structs wieder löschen.
    $iTmp = Random(0, UBound($aPTR)-1,1)
    _DeleteStruct($aPTR[$iTmp])
    _ArrayDelete($aPTR, $iTmp)
    Next

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

    Func _SaveStruct($vStruct) ; Sichert eine Struct im Array.
    $__Save_Anzahl += 1

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

    Local $iUbound = UBound($__Save_Array)

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

    If $iUbound < $__Save_Anzahl Then
    ReDim $__Save_Array[$iUbound + $__Save_Intervall]
    ConsoleWrite('Array wurde vergrößert: ' & $iUbound + $__Save_Intervall & @CRLF)
    EndIf

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

    _Display($__Save_Array, 'Speichern', $__Save_Anzahl - 2)

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

    $__Save_Array[$__Save_Anzahl-1] = $vStruct

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

    EndFunc

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

    Func _DeleteStruct($iPointer) ; Löscht eine Struct mittels Pointer

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

    ConsoleWrite('Löschen: ' & $iPointer & @CRLF)

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

    For $i = 0 To UBound($__Save_Array) - 1 Step 1
    If $iPointer = DllStructGetPtr($__Save_Array[$i]) Then
    $__Save_Array[$i] = 0 ; Struct löschen
    __Ordnen($i)
    Return 1 ; 1 = Alles OK
    EndIf
    Next

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

    ConsoleWrite('! ERROR: Struct an der Stelle: ' & $iPointer & ' nicht gefunden...' & @CRLF)

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

    EndFunc

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

    Func __Ordnen($iLeer) ; Sortiert etwas. Wird noch in die Löschen Funktion integriert.

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

    ConsoleWrite('Löschen: ' & $iLeer & @CRLF)
    ConsoleWrite('Letztes: ' & $__Save_Anzahl - 1 & @CRLF)

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

    ; Alle Arrayelemente so an, dass keine Lücken enstehen.
    ; Da nur ein Element Leer sein kann (Aufruf nur durch Delete)
    ; wird einfach das Letzte belegte Element dahin verschoben.

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

    _Display($__Save_Array, 'vorher', $__Save_Anzahl - 1)

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

    $__Save_Anzahl -= 1 ; Es gibt eine Struct weniger jetzt.
    $__Save_Array[$iLeer] = $__Save_Array[$__Save_Anzahl] ; Austausch.
    $__Save_Array[$__Save_Anzahl] = 0 ; Alte Struct löschen.

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

    If ($__Save_Anzahl + $__Save_Intervall) < UBound($__Save_Array) Then
    ReDim $__Save_Array[$__Save_Anzahl+1]
    ConsoleWrite('Array wurde verkleinert: ' & $__Save_Anzahl+1 & @CRLF)
    EndIf

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

    _Display($__Save_Array, 'nachher', $iLeer)

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

    EndFunc

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

    Func _Display($a, $p = 'vorher', $iTmp = -1) ; Gibt eine Liste der Pointer der Structs im Array aus.
    Local $s = '|---------- ' & $p & @CRLF, $u = UBound($a)
    For $i = 0 To $u - 1 Step 1
    If $i = $iTmp Then $s &= '> '
    $s &= $i & ' ' & DllStructGetPtr($a[$i]) & @CRLF
    Next
    $s &= '|----------' & @CRLF & @CRLF
    ConsoleWrite($s)
    EndFunc

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

    Func _ptr() ; erstellt eine Struct und gibt anschließend nur den Pointer raus.

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

    Local $vStruct = DllStructCreate('int[2]'), $iPointer
    DllStructSetData($vStruct, 1, 1000, 1)
    $iPointer = DllStructGetPtr($vStruct)
    _SaveStruct($vStruct)

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

    Return $iPointer

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

    EndFunc

    [/autoit]

    Edit:
    Habe das Skript jetzt etwas in Form gebracht und das Beispiel etwas umgebaut, damit es als "richtiges" Beispiel nutzbar ist.
    Dateien im Anhang (da es 2 sind. Und so viele Spoiler machen keinen Spaß)
    Optimal wäre eine freundliche Person mit guten Englischkenntnissen die Lust hat alles mal durchzulesen :thumbup:

    • Offizieller Beitrag

    Das Array im Array dürfte jedem ein Begriff sein.
    Leider funktionniert Struct im Struct bei AutoIt nicht, warum ?


    Naja...
    Man kann schon Struktur in einer Struktur speichern. Kommt nur drauf an, wie man es verwaltet. Ein Pointerarray macht sich dafür recht gut.
    Hier mal ein Bsp.:

    Spoiler anzeigen
    [autoit]

    ; == 3 Verschiedene Strukturen in einer MainStruktur speichern ==

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

    $iElements = 9 ;<== Summe der Elemente in den zu speichernden Strukturen
    $struct1 = 'int;int;'
    $shift1 = 0 ;<== shift gibt den Versatz der Struktur in der Mainstruktur an = Summe vorherig angeordneter Strukturelemente
    $struct2 = 'int;byte;char[5];'
    $shift2 = 2
    $struct3 = 'hwnd;hwnd;int;int;'
    $shift3 = 5

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

    Global $aStruct = DllStructCreate('ptr[' & $iElements & '];' & $struct1 & $struct2 & $struct3)
    For $i = 1 To $iElements
    DllStructSetData($aStruct, 1, DllStructGetPtr($aStruct, $i +1), $i)
    Next

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

    ; == in Strukturen schreiben ==

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

    ; == struct1
    _DllArrayStructSetData($aStruct, 0, 1, 64)
    _DllArrayStructSetData($aStruct, 0, 2, 128)
    ; == struct2
    _DllArrayStructSetData($aStruct, 2, 1, 30)
    _DllArrayStructSetData($aStruct, 2, 2, 8)
    _DllArrayStructSetData($aStruct, 2, 3, 'hallo')
    ; == struct3
    _DllArrayStructSetData($aStruct, 5, 1, WinGetHandle('') )
    _DllArrayStructSetData($aStruct, 5, 2, 0x135790)
    _DllArrayStructSetData($aStruct, 5, 3, 256)
    _DllArrayStructSetData($aStruct, 5, 4, 512)

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

    ; == aus Strukturen lesen ==

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

    ; == struct1
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct1, 0, 1) & @CRLF )
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct1, 0, 2) & @CRLF )
    ; == struct2
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct2, 2, 1) & @CRLF )
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct2, 2, 2) & @CRLF )
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct2, 2, 3) & @CRLF )
    ; == struct3
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct3, 5, 1) & @CRLF )
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct3, 5, 2) & @CRLF )
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct3, 5, 3) & @CRLF )
    ConsoleWrite( _DllArrayStructGetData($aStruct, $struct3, 5, 4) & @CRLF )

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

    Func _DllArrayStructSetData(ByRef $_aStruct, $_iShift, $_iElement, $_value, $_iIndex=0)
    If $_iIndex > 0 Then
    Return DllStructSetData($_aStruct, 1 + $_iShift + $_iElement, $_value, $_iIndex)
    Else
    Return DllStructSetData($_aStruct, 1 + $_iShift + $_iElement, $_value)
    EndIf
    EndFunc

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

    Func _DllArrayStructGetData(ByRef $_aStruct, $_sStruct, $_iShift, $_iElement, $_iIndex=0)
    If StringRight($_sStruct, 1) = ';' Then $_sStruct = StringTrimRight($_sStruct, 1)
    Local $aElements = StringSplit($_sStruct, ';')
    Local $structElement = $aElements[$_iElement]
    Local $ptr = DllStructGetData($_aStruct, 1, $_iShift + $_iElement)
    Local $tmpStruct = DllStructCreate($structElement, $ptr)
    If $_iIndex > 0 Then
    Return DllStructGetData($tmpStruct, 1, $_iIndex)
    Else
    Return DllStructGetData($tmpStruct, 1)
    EndIf
    EndFunc

    [/autoit]
  • Gut mein Beispiel war schlecht gewählt, da es eine vermutlich leichtere Lösung auf anderem Wege gibt.

    Es geht im Prinzip darum, dass Structs nicht immer gelöscht werden.
    Wozu man diese nicht gelöschten Structs man verwendet ist jedem selbst überlassen.

    Danke aber, dass du es dir angeschaut hast :)