Arbeiten mit Dll's in AutoIt
Datentypen

AutoIt selbst nimmt uns die Verwaltung von Datentypen ab, es gibt nur einen Typ (Variant), der funktionsgemäß verwendet wird.
Das ist bei der Arbeit mit Dll-Aufrufen anders. Hier ist eine Angabe der verwendeten Datentypen zwingend erforderlich.

Gültige Datentypen für einen Dll Call
Typ Details
none kein Wert (nur gültig für Rückgabetyp - entspricht void in C)
BYTE 8Bit (1Byte) Integer vorzeichenlos
BOOLEAN 8Bit (1Byte) Integer vorzeichenlos
short 16Bit (2Byte) Integer
USHORT 16Bit (2Byte) Integer vorzeichenlos
WORD 16Bit (2Byte) Integer vorzeichenlos
int 32Bit (4Byte) Integer
long 32Bit (4Byte) Integer
BOOL 32Bit (4Byte) Integer
UINT 32Bit (4Byte) Integer vorzeichenlos
ULONG 32Bit (4Byte) Integer vorzeichenlos
DWORD 32Bit (4Byte) Integer vorzeichenlos
INT64 64Bit (8Byte) Integer
UINT64 64Bit (8Byte) Integer vorzeichenlos
ptr ein allgemeiner Pointer (void *)
HWND ein Fenster Handle (Pointer)
HANDLE ein Handle (Pointer)
float eine Kommazahl einfacher Genauigkeit
double eine Kommazahl doppelter Genauigkeit
INT_PTR, LONG_PTR, LRESULT, LPARAM ein Integer, groß genug um einen Pointer zu speichern, wenn eine x86 oder x64 Versions von AutoIt läuft
UINT_PTR, ULONG_PTR, DWORD_PTR, WPARAMein unsigned Integer, groß genug um einen Pointer zu speichern, wenn eine x86 oder x64 Versions von AutoIt läuft
str ein ANSI String (Speicher für 65536 Zeichen wird reserviert)
wstr ein UNICODE wide character String (Speicher für 65536 Zeichen wird reserviert)
struct eine mit DllStructCreate() erstellte Struktur
* Füge * an das Ende eines anderen Typs um ihn ByRef zu erstellen. Z.B.: "int*" erstellt einen Pointer zu einem "int" Typ.

< zum Anfang >

Umwandlung von Windows API-Typen zu AutoIt-Typen
WINDOWS API Type AutoIt Type
LPCSTR/ LPSTR str
LPCWSTR/ LPWSTR wstr
LPVOID ptr
LPxyz xyz*
HINSTANCE handle
HRESULT long
LONGLONG/ LARGE_INTEGER INT64
ULONGLONG/ ULARGE_INTEGERUINT64
SIZE_T ULONG_PTR

Eigentlich fehlen noch 2 Typen:
Für ANSI-Zeichen "char" und für UNICODE "wchar". Wir finden diese aber in der Auflistung der Strukturdatentypen.
Beide Typen repräsentieren 1 einzelnes Zeichen. Wie lassen sich damit aber Zeichenketten darstellen? Dazu definiert man ein Array von Zeichen (z.B. char[20]). Wichtig bei der Verwendung von UNICODE: Zeichenketten werden dort NULL-terminiert, d.h. nach den Zeichen des Strings folgt der ASCII-Wert 0 um anzuzeigen, dass hier der String endet. Somit muss die Anzahl von "wchar" um 1 größer sein als die Stringlänge. Wenn in der Funktionsbeschreibung angegeben wird, dass der String nullterminiert ist, gilt das ebenso bei ANSI.
Allerdings erleichtert uns AutoIt die Verwaltung dieser beiden Typen durch Verwendung von "str" und "wstr".
Da erst ab Windows 2000 volle UNICODE Unterstützung existiert, sind fast alle Stringfunktionen mit ANSI- und UNICODE- Version ausgestattet. Sie werden unterschieden durch den Zusatz "A" für ANSI-Funktion und "W" (wide char) für UNICODE-Funktion. Also "StringFunktionA" und "StringFunktionW".

Wenn ihr nicht gerade für Win98 programmiert (ha-ha), ist es eigentlich auch nicht sinnvoll, die ANSI-Varianten der Funktionen zu verwenden. Mein Tipp: Setzt nach Möglichkeit die UNICODE-Variante ein.

< zum Anfang >

Strukturen

Wenn wir mit Dll arbeiten wollen, müssen wir auch über Strukturen reden.
Was also ist eine Struktur?
Eine Struktur weist Ähnlichkeit mit einem Array auf. D.h., unter einem Variablennamen werden 1 oder mehrere Werte gespeichert.
Was sind die Unterschiede?
Im Array ist es egal, was für Datentypen die einzelnen Werte haben. Sie können dort auch ständig wechseln. Die Anzahl der Element in einem Array ist niemals fix, mit ReDim läßt sich die Größe zur Laufzeit ändern.
Ganz anders die Struktur. Hier bestimme ich beim Erstellen der Struktur, welche Datentypen die Inhalte haben dürfen. Das ist wichtig, da in den Dll-Funktionen genau festgelegt ist, welche Datentypen an welcher Position erwartet werden. Die Anzahl der Strukturelemente kann zur Laufzeit nicht verändert werden.

AutoIt hat schon eine Vielzahl von Strukturen implementiert. In der Hilfe findet ihr diese unter <User Defined Functions> <StructureConstants Management>.
Als Hauptinformationsquelle verwende ich die Windows Api Referenz. Dort findet ihr neben allen API-Funktionen die zugehörigen Messages, Notifikationen, Strukturen und Konstanten.

Struktur Erstellen

Zum Erstellen einer Struktur stehen in AutoIt folgende Datentypen zur Verfügung

Type Details
BYTE 8Bit (1Byte) Zeichen vorzeichenlos
BOOLEAN 8Bit (1Byte) Zeichen vorzeichenlos
CHAR 8Bit (1Byte) ASCII-Zeichen
WCHAR 16Bit (2Byte) UNICODE-Zeichen, (wide char)
short 16Bit (2Byte) Integer mit Voreichen
USHORT 16Bit (2Byte) Integer vorzeichenlos
WORD 16Bit (2Byte) Integer vorzeichenlos
int 32Bit (4Byte) Integer mit Vorzeichen
long 32Bit (4Byte) Integer mit Vorzeichen
BOOL 32Bit (4Byte) Integer mit Vorzeichen
UINT 32Bit (4Byte) Integer vorzeichenlos
ULONG 32Bit (4Byte) Integer vorzeichenlos
DWORD 32Bit (4Byte) Integer vorzeichenlos
INT64 64Bit (8Byte) Integer mit Vorzeichen
UINT64 64Bit (8Byte) Integer vorzeichenlos
ptr 32 oder 64Bit Integer vorzeichenlos (in Abhängigkeit zur benutzten AutoIt Version x86 oder x64)
HWND 32Bit (4Byte) Integer
HANDLE 32Bit (4Byte) Integer
float 32Bit (4Byte) Fließkommazahl
double 64Bit (8Byte) Fließkommazahl
INT_PTR, LONG_PTR,
LRESULT, LPARAM
32 oder 64Bit Integer mit Vorzeichen (in Abhängigkeit zur benutzten AutoIt Version x86 oder x64)
UINT_PTR, ULONG_PTR,
DWORD_PTR, WPARAM
32 oder 64Bit Integer vorzeichenlos (in Abhängigkeit zur benutzten AutoIt Version x86 oder x64)
STRUCT Die nachfolgenden Datentypen werden ausgerichtet nach den Regeln der C-Deklaration
ENDSTRUCT Ende einer Sammlung von Datentypen
ALIGN n-Bytes Begrenzung an der Datentypen ausgerichtet werden

Eine Struktur erstellen wir mit:

$struct = DllStructCreate ( "Struct" [, Pointer ] )

In der allgemeinen Definition ist als optionaler Parameter noch Pointer angegeben. Darauf gehe ich im Punkt Pointer näher ein.
Die Struktur wird in einem String definiert. Der String kann neben dem Datentyp auch noch einen Bezeichner führen. Dieser ist aber nicht zwingend erforderlich.
Im folgenden Bsp. enthält die Struktur 4 Integerwerte. Die Definition eines Wertes lautet: Datentyp-Leerzeichen-Bezeichner, die Abgrenzung zwischen den Werten erfolgt durch ein Semikolon.

$struct = DllStructCreate ( "int X1;int Y1;int X2;int Y2" )

Und zum Vergleich die identische Struktur, ohne Bezeichner.

$struct = DllStructCreate ( "int;int;int;int" )

< zum Anfang >

In eine Struktur schreiben

Um Werte an eine Struktur zu übergeben, verwenden wir die Funktion

DllStructSetData ( Struct, Element, value [, index ] )
Structist die Variable, der mit "DllStructCreate" erstellten Struktur (oder eine Struktur aus einem Funktionsaufruf)
Elementist entweder der 1-basierte Index des Wertes in der Struktur oder der Bezeichner
valueist der zu übergebende Wert
index[optional] ist das Element ein Array, kann auf ein einzelnes Arrayelement mit diesem 1-basierten Index zugegriffen werden

Mit Einführung der Version 3.3.10.0 gab es Syntaxänderungen. Dadurch ist ein geänderter Zugriff auf Strukturelemente möglich. Die Bsp. sind dahingehend angepasst.
Die Verwendung der Punktnotation (Strukturname.Feld) ist zwar syntaktisch möglich, ganz offiziell aber kein sicherer Zugriffsstandard. Sollte es also damit mal nicht klappen, ist auf die Standardsyntax zurückzugreifen.

$struct = DllStructCreate ( "int X1;int Y1;int X2;int Y2" )

DllStructSetData ( $struct, "X1", 20 )
DllStructSetData ( $struct, "Y1", 10 )
DllStructSetData ( $struct, "X2", 200 )
DllStructSetData ( $struct, "Y2", 120 )
; oder
DllStructSetData ( $struct, 1, 20 )
DllStructSetData ( $struct, 2, 10 )
DllStructSetData ( $struct, 3, 200 )
DllStructSetData ( $struct, 4, 120 )
; oder (ab v3.3.10.0)
$struct.X1 = 20
$struct.Y1 = 10
$struct.X2 = 200
$struct.Y2 = 120

; hier ist kein Bezeichner definiert, somit kann nur der Index verwendet werden
$struct1 = DllStructCreate ( "int;int;int;int" )

DllStructSetData ( $struct1, 1, 20 )
DllStructSetData ( $struct1, 2, 10 )
DllStructSetData ( $struct1, 3, 200 )
DllStructSetData ( $struct1, 4, 120 )

$struct2 = DllStructCreate ( "char[6]" )

; entweder
DllStructSetData ( $struct2, 1, "AutoIt" )
; oder
DllStructSetData ( $struct2, 1, "A", 1 )
DllStructSetData ( $struct2, 1, "u", 2 )
DllStructSetData ( $struct2, 1, "t", 3 )
DllStructSetData ( $struct2, 1, "o", 4 )
DllStructSetData ( $struct2, 1, "I", 5 )
DllStructSetData ( $struct2, 1, "t", 6 )

< zum Anfang >

Aus einer Struktur lesen

Um Werte aus einer Struktur zu lesen, verwenden wir die Funktion

$data = DllStructGetData ( Struct, Element [, index ] )
Struct ist die Variable, der mit "DllStructCreate" erstellten Struktur (oder eine Struktur aus einem Funktionsaufruf)
Elementist entweder der 1-basierte Index des Wertes in der Struktur oder der Bezeichner
index [optional] ist das Element ein Array, kann auf ein einzelnes Arrayelement mit diesem 1-basierten Index zugegriffen werden

Diese Funktion ist das exakte Gegenstück zu "DllStructSetData".

$struct2 = DllStructCreate ( "char[6]" )
DllStructSetData ( $struct2, 1, "AutoIt" )

; Ausgabe der Werte
; entweder
$data = DllStructGetData ( $struct2, 1 )    ; liefert "AutoIt"
; oder
$data = DllStructGetData ( $struct2, 1, 1 ) ; liefert "A"
$data = DllStructGetData ( $struct2, 1, 2 ) ; liefert "u"
$data = DllStructGetData ( $struct2, 1, 3 ) ; liefert "t"
$data = DllStructGetData ( $struct2, 1, 4 ) ; liefert "o"
$data = DllStructGetData ( $struct2, 1, 5 ) ; liefert "I"
$data = DllStructGetData ( $struct2, 1, 6 ) ; liefert "t"

< zum Anfang >

Pointer

Bei dem Pointer handelt es sich nicht um die Jagdhundrasse Pointer ( :P Scherz am Rande ), sondern um eine Variable, die auf eine Speicheradresse verweist. Ich hatte ja bereits beim Erstellen der Struktur darauf hingewiesen.
Nehmen wir an, irgendeine WM_Message liefert uns über den Parameter "lParam" Informationen zu einer Desktopkoordinate. Aus der Beschreibung zu der Message wissen wir, dass die Werte in der Datenstruktur "int;int" übergeben werden. Wir müssen also beim Erstellen der Struktur den Pointer mit angeben, sodass die Werte, auf die der Pointer verweist, an die Struktur übergeben werden können.
So könnte das dann aussehen:

Func MyMessage ( $hWnd, $Msg, $wParam, $lParam )
Local $struct = DllStructCreate ( "int;int", $lParam ) ; lParam wird als Pointer übergeben
ConsoleWrite ( "Koordinate X = " & DllStructGetData ( $struct, 1 ) & @LF )
ConsoleWrite ( "Koordinate Y = " & DllStructGetData ( $struct, 2 ) & @LF )
;....
;....
EndFunc

Im umgekehrten Fall haben wir eine Struktur und die Funktion erwartet einen Pointer dafür. Somit müssen wir einen Pointer erstellen, der auf diese Struktur verweist. Der Pointer kann entweder auf die gesamte Struktur oder auf ein einzelnes Element der Struktur verweisen. Dafür nutzen wir in AutoIt die Funktion

$pointer = DllStructGetPtr ( Struct [, Element ] )
Struct ist die Variable, der mit "DllStructCreate" erstellten Struktur (oder eine Struktur aus einem Funktionsaufruf)
Element[optional] der 1-basierte Index des Elementes aus der Struktur auf das der Pointer zeigen soll

Nehmen wir an, wir haben folgende Struktur: "int;int;int;ptr", wobei der Pointer auf eine "int;int"-Struktur verweisen soll. Das sieht dann so aus:

$struct = DllStructCreate ( "int;int" )                  ; Struktur auf die der Pointer zeigen soll
$pointer = DllStructGetPtr ( $struct )                   ; Pointer für die Struktur
$struct1 = DllStructCreate ( "int;int;int" & $pointer )  ; Struktur mit Pointer

< zum Anfang >

Dll Call

So sieht ein DllCall in AutoIt allgemein aus:

DllCall ( "DLL", "Rückgabetyp", "Funktionsname" [,"Parametertyp", "Parametername", ...] )
DLL die Dll, die wir nutzen möchten
Rückgabetypder Datentyp, den der Dll-Aufruf zurückliefert
Funktionsname die Funktion aus der Dll, die wir aufrufen möchten
Parametertyp [optional] Datentyp des Parameters
Parametername [optional] Name des Parameters
... [optional] weitere Parametertypen/ -namen

Woher weiß ich, ob mein Aufruf erfolgreich war?
Dazu werte ich die Rückgabe des DllCall aus. Hierbei ist folgendes zu beachten:
Die Funktion DllCall() liefert ein Array zurück. Das Array enthält als erstes Element (Array[0]) den Ergebniswert des DllCall. Dieser ist im Fehlerfall = 0, ansonsten <> 0. Für jeden Parameter des Aufrufs wird ein Arrayelement erstellt. Sofern dieser Parameter vom Aufruf nicht verändert wird, ist also der übergebene Parameterwert auch im Ergebnisarray.
Für die Fehlerabfrage empfiehlt sich also:

$return = DllCall (....)
If $return[0] = 0 Then ; Fehler

< zum Anfang >

Array (1D) im DllCall - Pointerarray

Es kommt vor, dass ein Dll-Aufruf als Element ein Array zur Übergabe/Übernahme von Werten verlangt.
Einen Strukturdatentyp "array" haben wir nicht zur Verfügung. Aber wir wissen, dass Datentypen in Arrayform übergeben werden können: "typ[anzahl]".
Somit bietet sich an, ein Pointerarray zu erstellen, dessen Elemente jeweils auf einen Wert unseres Datenarrays verweisen.
Die Struktur sieht dann folgendermaßen aus:
"Pointer-Array[Anzahl_Arrayelemente];je_Arrayelement_ein_Strukturelement...."

Nehmen wir an, das ist unsere Dll-Definition:

DLL Meine.Dll
Funktion IrgendWas
Returntyp long
Parameter ElementName
Parameter TypString Array

Die Strings als Zeichenketten im Array

; Bsp.: Array Zeichenkette
Local $countElements = 2
Local $maxStringLen = 255
Local $structChar = "ptr[" & $countElements & "];" & _
"char[" & $maxStringLen & "];" & _
"char[" & $maxStringLen & "]"
Local $arrChar = DllStructCreate ( $structChar )

Die Struktur ist erstellt und wird nun mit Daten befüllt

; für jedes Arrayelement den Pointer setzen
DllStructSetData ( $arrChar, 1, DllStructGetPtr ( $arrChar, 2 ), 1 )
DllStructSetData ( $arrChar, 1, DllStructGetPtr ( $arrChar, 3 ), 2 )
; die Array-Werte eintragen
DllStructSetData ( $arrChar, 2, Element_1 )
DllStructSetData ( $arrChar, 3, Element_2 )

Und so sieht es dann in der Struktur aus:

ptr[2] char[255]char[255]
Pointer1E E
Pointer2 l l
e e
m m
e e
n n
t t
_ _
1 2

Jetzt übergeben wir das Array (bzw. den Pointer, der auf das Array verweist) an den DllCall.
In unserer Struktur $arrChar ist das erste Strukturelement das Array mit den Pointern, welche auf die eingetragenen Werte verweisen.

DllCall ( "Meine.DLL", "long", "IrgendWas", "ptr", DllStructGetPtr ( $arrChar, 1 ) )

Als 'Nachweis', dass tatsächlich über das Pointerarray auf die Werte zugegriffen wird, lesen wir die Daten aus, indem wir eine neue Struktur erstellen, die über die verwendeten Pointer befüllt wird.

; der an den DllCall übergebene Pointer für das Pointerarray
Local $ptrInDll = DllStructGetPtr ( $arrChar, 1 )

; Arraystruktur zum Lesen nachstellen
Local $structPtr = DllStructCreate ( "ptr[2]", $ptrInDll )

; Pointer des Wertearrays auslesen
Local $ptrElement1 = DllStructGetData ( $structPtr, 1, 1 )
Local $ptrElement1 = DllStructGetData ( $structPtr, 1, 2 )

; Struktur zum Lesen der Werte erstellen und aus Pointern befüllen
Local $structRead
$structRead = DllStructCreate ( "char[" & $maxStringLen & "]", $ptrElement1 )
ConsoleWrite ( DllStructGetData ( $structRead, 1 ) & @LF )
$structRead = DllStructCreate ( "char[" & $maxStringLen & "]", $ptrElement2 )
ConsoleWrite ( DllStructGetData ( $structRead, 1 ) & @LF )

< zum Anfang >

Array (1D) im DllCall - Bytearray

Häufig verlangen Funktionen kein Pointerarray, sondern ein Bytearray. Hier zeige ich, wie sich das umsetzen läßt.

; Bsp.: Integer-Array mit 5 Elementen

; == 1. Speichergröße für ein Element ermitteln
$SIZE = DllStructGetSize ( DllStructCreate ( "int", 1 ) )
; die "1" ist keine wirkliche Pointeradresse, sondern wird nur als Basis für die Berechnung genutzt

; == 2. Byte-Array mit gewünschter Anzahl Elementen erstellen
$aByte = DllStructCreate ( "byte[" & $SIZE *5 & "]" )

; == 3. Strukturen für jedes Arrayelement erstellen
$Element_0 = DllStructCreate ( "int", DllStructGetPtr ( $aByte ) )
$Element_1 = DllStructCreate ( "int", DllStructGetPtr ( $aByte + $SIZE ) )
$Element_2 = DllStructCreate ( "int", DllStructGetPtr ( $aByte + $SIZE *2 ) )
$Element_3 = DllStructCreate ( "int", DllStructGetPtr ( $aByte + $SIZE *3 ) )
$Element_4 = DllStructCreate ( "int", DllStructGetPtr ( $aByte + $SIZE *4 ) )

; == Alternativ die Strukturen in einem Datenarray führen

Local $aStructData[4]
For $i = 0 To 4
$aStructData[$i] = DllStructCreate ( "int", DllStructGetPtr ( $aByte + $SIZE *$i ) )
Next

; == 4. Werte eintragen
DllStructSetData ( $aStructData[0], 1, 123 ; usw. für die anderen Elemente

Das so erstellte und bei Bedarf mit Werten befüllte Array wird dann über einen Pointer an die Dll übergeben

DllCall ( "xyz.dll", "long", "Eine_Funktion", "ptr", DllStructGetPtr ( $aByte ) )

< zum Anfang >

Beispiele

Beep( )

Wir werden jetzt die Funktion Beep( ) als DllCall ausführen. Die Funktion verwendet 2 Parameter: Frequenz und Dauer. Wir führen den Aufruf mit 500 Hz über 700 ms aus.
Hier die Originalbeschreibung aus der Windows API-Referenz:

Declare Function Beep Lib "kernel32" Alias "Beep" (ByVal dwFreq As Long, ByVal dwDuration As Long) As Long
• dwFreq
Specifies the frequency, in hertz, of the sound. This parameter must be in the range 37 through 32,767 (0x25 through 0x7FFF).
• dwDuration
Specifies the duration, in milliseconds, of the sound.

Die Funktion wird hier in folgender Form dargestellt:

"Funktionsname" "DLL" ["Alias-Funktionsname"] ("Parametername", "Parametertyp", ...) Rückgabetyp

Der Dll Aufruf in AutoIt erwartet, wie schon gezeigt, folgende Darstellung:

DllCall ( "DLL", "Rückgabetyp", "Funktionsname" [,"Parametertyp", "Parametername", ...] )

Suchen wir uns die notwendigen Angaben zusammen:

DLL kernel32
Rückgabetyplong
Funktionsname Beep (der Alias braucht nur verwendet werden, wenn er vom Funktionsnamen abweicht)
Parametertyp long
Parametername dwFreq (500)
Parametertyp long
Parametername dwDuration (700)

Und so sieht nun der fertige Aufruf aus:

DllCall ( "kernel32", "long", "Beep", "long", 500, "long", 700 )

< zum Anfang >

MsgBox "Hallo Welt!"

Wir wollen per MsgBox den Text "Hallo Welt!" ausgeben.
Hier wiederum die Infos aus der Windows API-Referenz zur MsgBox:

Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long
• hWnd
Identifies the owner window of the message box to be created. If this parameter is NULL, the message box has no owner window.
• lpText
Points to a null-terminated string containing the message to be displayed.
• lpCaption
Points to a null-terminated string used for the dialog box title. If this parameter is NULL, the default title Error is used.
• uType
Specifies a set of bit flags that determine the contents and behavior of the dialog box.

hWnd hier können wir 0 übergeben, da wir die Nachricht allgemein anzeigen
lpText hier kommt unser Text rein: "Hallo Welt!"
lpCaptiondas ist der Titel der MsgBox, wir nehmen: "Test"
uType wir brauchen nur den OK-Button, das ist die 0

So sieht es fertig aus:

DllCall ( "user32", "long", "MessageBoxA", "hwnd", 0, "str", "Hallo Welt!", "str", "Test", "long", 0 )

Ihr seht in dieser Funktion den Suffix "A" am Funktionsnamen. Für UNICODE wäre also folgender Aufruf zuständig.

DllCall ( "user32", "long", "MessageBoxW", "hwnd", 0, "wstr", "Hallo Welt!", "wstr", "Test", "long", 0 )

; Wenn der Standardtitel für die Messagebox verwendet werden soll, steht ab v3.3.10.0 das Schlüsselwort Null zur Verfügung
DllCall ( "user32", "long", "MessageBoxW", "hwnd", 0, "wstr", "Hallo Welt!", "wstr", Null, "long", 0 )

< zum Anfang >

GetVolumeInformation

Nun zu einer Funktion, die uns mehrere Ergebniswerte liefert.
Wieder die Beschreibung aus der API-Referenz:

Declare Function GetVolumeInformation Lib "kernel32" Alias "GetVolumeInformationW" (ByVal lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Long, lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long, lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, ByVal nFileSystemNameSize As Long) As Long
• lpRootPathName
Points to a string that contains the root directory of the volume to be described. If this parameter is NULL, the root of the current directory is used. If this parameter is a UNC name, you must follow it with an additional backslash. For example, you would specify \\MyServer\MyShare as \\MyServer\MyShare\.
• lpVolumeNameBuffer
Points to a buffer that receives the name of the specified volume.
• nVolumeNameSize
Specifies the length, in characters, of the volume name buffer. This parameter is ignored if the volume name buffer is not supplied.
• lpVolumeSerialNumber
Points to a variable that receives the volume serial number. This parameter can be NULL if the serial number is not required.
• lpMaximumComponentLength
Points to a doubleword value that receives the maximum length, in characters, of a filename component supported by the specified file system. A filename component is that portion of a filename between backslashes. The value stored in variable pointed to by *lpMaximumComponentLength is used to indicate that long names are supported by the specified file system. For example, for a FAT file system supporting long names, the function stores the value 255, rather than the previous 8.3 indicator. Long names can also be supported on systems that use the New Technology file system.
• lpFileSystemFlags
Points to a doubleword that receives flags associated with the specified file system.
• lpFileSystemNameBuffer
Points to a buffer that receives the name of the file system (such as FAT or NTFS).
• nFileSystemNameSize
Specifies the length, in characters, of the file system name buffer. This parameter is ignored if the file system name buffer is not supplied.

Hier gehts nun richtig zur Sache. :=)
In den Parameterbeschreibungen findet ihr fast überall: "Points to ...". Das bedeutet für uns, dass auf einen Pointer verwiesen wird, der zur Übergabe/Übernahme von Werten benötigt wird.
Wie das umzusetzen ist, haben wir ja schon bei den Themen Struktur und Pointer kennengelernt.

Hier die Vorbereitung der Parameter:

; das Ziel unserer Abfrage
$Vol = "C:\"
; Erstellen der Struktur zur Übergabe (1 größer als Stringlänge!)
$lpRootPathName = DllStructCreate ( "wchar[" & StringLen ( $Vol ) +1 & "]" )
; Füllen der Struktur
DllStructSetData ( $lpRootPathName, 1, $Vol )

; Erstellen der Struktur zur Aufnahme des VolumeNamens
$lpVolumeNameBuffer = DllStructCreate ( "wchar[255]" )
; Angabe der Länge des Aufnahmepuffers für VolumeName
$nVolumeNameSize = 255

; Erstellen der Struktur zur Aufnahme der Seriennummer
$lpVolumeSerialNumber = DllStructCreate ( "wchar[255]" )

; Erstellen der Struktur zur Aufnahme der max. Komponentenlänge
$lpMaximumComponentLength = DllStructCreate ( "dword" )

; Erstellen der Struktur zur Aufnahme der FileSystemFlags
$lpFileSystemFlags = DllStructCreate ( "dword" )

; Erstellen der Struktur zur Aufnahme des FileSystemNamens
$lpFileSystemNameBuffer = DllStructCreate ( "wchar[255]" )

; Angabe der Länge des Aufnahmepuffers für FileSystemName
$nFileSystemNameSize = 255

Das ist dann der zugehörige Aufruf:

DllCall ( "kernel32", "long", "GetVolumeInformationW", _
"ptr" , DllStructGetPtr ( $lpRootPathName ), _
"ptr" , DllStructGetPtr ( $lpVolumeNameBuffer ), _
"long", $nVolumeNameSize, _
"ptr" , DllStructGetPtr ( $lpVolumeSerialNumber ), _
"ptr" , DllStructGetPtr ( $lpMaximumComponentLength ), _
"ptr" , DllStructGetPtr ( $lpFileSystemFlags ), _
"ptr" , DllStructGetPtr ( $lpFileSystemNameBuffer ), _
"long", $nFileSystemNameSize )

Und hier die Auswertung der ermittelten Daten:

; Nach dem DllCall sind die ermittelten Werte in den über die Pointer adressierten Strukturen enthalten.
; Um auf die Werte zuzugreifen, werden diese mit "DllStructGetData( )" ausgelesen.

ConsoleWrite ( "Volume name: "           & DllStructGetData ( $lpVolumeNameBuffer, 1 ) & @LF )
ConsoleWrite ( "Serial number: "         & DllStructGetData ( $lpVolumeSerialNumber, 1 ) & @LF )
ConsoleWrite ( "Max. Component Length: " & DllStructGetData ( $lpMaximumComponentLength, 1 ) & @LF )
ConsoleWrite ( "File System: "           & DllStructGetData ( $lpFileSystemNameBuffer, 1 ) & @LF )
ConsoleWrite ( "File System Flags: "     & _checkFSflags ( DllStructGetData ( $lpFileSystemFlags, 1 ) ) & @LF )

; Hilfsfunktion zum Prüfen der Flags
Func _checkFSflags ( $flagsum )
Local Const $FS_CASE_IS_PRESERVED      = 0x2
Local Const $FS_CASE_SENSITIVE         = 0x1
Local Const $FS_UNICODE_STORED_ON_DISK = 0x4
Local Const $FS_PERSISTENT_ACLS        = 0x8
Local Const $FS_FILE_COMPRESSION       = 0x10
Local Const $FS_VOL_IS_COMPRESSED      = 0x8000
Local $sFlags = @LF
If BitAnd ( $flagsum, $FS_CASE_IS_PRESERVED ) Then
$sFlags &= @TAB & "FS_CASE_IS_PRESERVED" & @LF
EndIf
If BitAnd ( $flagsum, $FS_CASE_SENSITIVE ) Then
$sFlags &= @TAB & "FS_CASE_SENSITIVE" & @LF
EndIf
If BitAnd ( $flagsum, $FS_UNICODE_STORED_ON_DISK ) Then
$sFlags &= @TAB & "FS_UNICODE_STORED_ON_DISK" & @LF
EndIf
If BitAnd ( $flagsum, $FS_PERSISTENT_ACLS ) Then
$sFlags &= @TAB & "FS_PERSISTENT_ACLS" & @LF
EndIf
If BitAnd ( $flagsum, $FS_FILE_COMPRESSION ) Then
$sFlags &= @TAB & "FS_FILE_COMPRESSION" & @LF
EndIf
If BitAnd ( $flagsum, $FS_VOL_IS_COMPRESSED ) Then
$sFlags &= @TAB & "FS_VOL_IS_COMPRESSED" & @LF
EndIf
Return $sFlags
EndFunc

< zum Anfang >

GetCursorPos( )

Diese Funktion enthält als Parameter bereits eine Struktur.

Declare Function GetCursorPos Lib "user32" Alias "GetCursorPos" (lpPoint As POINTAPI) As Long
• lpPoint
Points to a POINT structure that receives the screen coordinates of the cursor.

Das Vorgehen für uns ist wie im letzten Bsp., die geforderte Struktur erstellen und mit einem Pointer darauf verweisen.
So sieht die enthaltene Struktur aus:

Type POINTAPI
x As Long
y As Long
End Type

Das übersetzen wir jetzt nach AutoIt:

$tPOINTAPI = DllStructCreate ( "long x; long y" )

DllCall ( "user32", "long", "GetCursorPos", "ptr", DllStructGetPtr ( $tPOINTAPI ) )

; die Werte aus der Struktur auslesen
ConsoleWrite ( "x: " & DllStructGetData ( $tPOINTAPI, 1 ) & @LF )
ConsoleWrite ( "y: " & DllStructGetData ( $tPOINTAPI, 2 ) & @LF )

; alternativ Ausgabe ab v3.3.10.0
ConsoleWrite ( "x: " & $tPOINTAPI.x & @LF )
ConsoleWrite ( "y: " & $tPOINTAPI.y & @LF )

< zum Anfang >

"Fremd"-Nutzung einer Dll-Struktur

Durch die Punktnotation lassen sich Dll-Strukturen auch zu netten Syntaxdarstellungen verwenden.
Ich habe hier eine Funktion erstellt, ähnlich der Funktion _PathSplit.

$path = _Path('//host/share/folder/file.ext')
ConsoleWrite('kpl. Pfad             ' & $path.FullPath & @CRLF)
ConsoleWrite('Laufwerk              ' & $path.Drive & @CRLF)
ConsoleWrite('Ordner                ' & $path.Dir & @CRLF)
ConsoleWrite('Dateiname             ' & $path.File & @CRLF)
ConsoleWrite('Name Erweiterung      ' & $path.Ext & @CRLF)
ConsoleWrite('Datei mit Erweiterung ' & $path.FileExt & @CRLF)

; oder direkt nur ein Parameter:
ConsoleWrite('Dateiname ' & _Path(@ScriptFullPath).File & @CRLF)

;===================================================================================================
; Function Name....: _Path
; Description......: Splittet eine Pfadangabe in einzelne Bestandteile
; Parameter(s).....: $sPath Pfadangabe, Lw-basiert oder UNC, absolut oder relativ - alles möglich
; .................:         Pfad kann Laufwerk od. Laufwerk mit Ordner od. Lw/Ordner/Datei.suffix sein
; .................:         Pfad wird nicht auf Existenz geprüft
; Return Value(s)..: Struktur mit den Feldern:
; .................:         .FullPath  Der kpl. Pfad in Normalform
; .................:         .Drive     Das Laufwerk (ohne Backslash), bei UNC-Pfad der Hostname
; .................:         .Dir       Das Verzeichnis (ohne endenden Backslash)
; .................:         .File      Der Dateiname
; .................:         .Ext       Der Name der Erweiterung
; .................:         .FileExt   Dateiname.Erweiterung
; Author(s)........: BugFix ( autoit@bug-fix.info )
;===================================================================================================

Func _Path($sPath) 
    Local $sDrive, $sDirFile, $sDir, $sFile, $sExt, $sFileExt, $sTmp
    $sPath = DllCall('kernel32.dll', 'dword', 'GetFullPathNameW', 'wstr', $sPath, 'dword', 4096, 'wstr', '', 'ptr', 0)[3]
    Select
        Case StringLeft($sPath, 2) = '\\'
            $sDrive = StringLeft($sPath, StringInStr($sPath, '\', 0, 3))
        Case StringRegExp($sPath, '^[a-zA-Z]:')
            $sDrive = StringLeft($sPath, 3)
    EndSelect
    $sDirFile = StringTrimLeft($sPath, StringLen($sDrive))
    $sDir     = StringLeft($sDirFile, StringInStr($sDirFile, '\', 0, -1))
    $sTmp     = StringTrimLeft($sDirFile, StringLen($sDir))
    $sFileExt = StringInStr($sTmp, '.') ? $sTmp : ''
    If $sFileExt = '' Then $sDir = $sDirFile
    $sFile    = ($sFileExt <> '') ? StringLeft($sFileExt, StringInStr($sFileExt, '.', 0, -1) -1) : ''
    $sExt     = ($sFileExt <> '') ? StringTrimLeft($sFileExt, StringLen($sFile) +1) : ''

    Local $tPath, $tagPath = 'struct;' & _
    'char FullPath[' & StringLen($sPath)  & '];' & _
    'char Drive['    & StringLen($sDrive) & '];' & _
    'char Dir['      & ((StringLen($sDir)   > 0) ? StringLen($sDir) : 1)   & '];' & _
    'char File['     & ((StringLen($sFile)  > 0) ? StringLen($sFile) : 1)  & '];' & _
    'char Ext['      & ((StringLen($sExt)   > 0) ? StringLen($sExt) : 1)   & '];' & _
    'char FileExt['  & ((StringLen($sExt)   > 0) ? (StringLen($sFile)+StringLen($sExt)+1) : 1) & '];' & _
    'endstruct'
    $tPath = DllStructCreate($tagPath)
    DllStructSetData($tPath, 1, StringRegExpReplace($sPath, '\\$', ''))
    DllStructSetData($tPath, 2, StringRegExpReplace($sDrive, '\\$', ''))
    DllStructSetData($tPath, 3, StringRegExpReplace($sDir, '\\$', ''))
    DllStructSetData($tPath, 4, $sFile)
    DllStructSetData($tPath, 5, $sExt)
    DllStructSetData($tPath, 6, $sFileExt)
    Return $tPath
EndFunc  

< zum Anfang >

Euer BugFix        Kontakt: Mail BugFix


Für die Codedarstellung wurde der Font Source Code Pro verwendet. Der Font steht unter der "Open Font License" (Lizenzdatei).
Der Text wurde in Quattrocento Roman verfaßt, einem ebenfalls frei verwendbaren Font.