Binärkompatibilität

Dieses Dokument enthält einige Themen, die manchmal wichtig sind, wenn externe Bibliotheken verwendet werden oder Meldungen an ein Steuerelement oder Fenster gesendet werden.

Unicode vs. ANSI

Hinweis: Dieser Abschnitt baut auf Themen auf, die in anderen Teilen der Dokumentation behandelt werden: Zeichenketten (Strings), Zeichenkettenkodierung.

Innerhalb einer Zeichenkette (Textwert) ist der numerische Zeichencode und die Größe (in Byte) jedes einzelnen Zeichens von der Kodierung der Zeichenkette abhängig. Solche Details sind in der Regel nur für Skripte interessant, die folgende Aktionen durchführen:

AutoHotkey v2 verwendet nativ Unicode (UTF-16), aber einige externe Bibliotheken oder Fenstermeldungen können ANSI-Zeichenketten erfordern.

ANSI: Jedes Zeichen ist ein Byte groß (8 Bits). Zeichencodes über 127 sind abhängig von den Spracheinstellungen Ihres Systems (oder von der Codepage, die bei der Kodierung des Textes gewählt wurde, z.B. beim Schreiben in eine Datei).

Unicode: Jedes Zeichen ist zwei Bytes groß (16 Bits). Zeichencodes sind durch das UTF-16-Format definiert.

Semantischer Hinweis: Technisch gesehen sind einige Unicode-Zeichen durch zwei 16-Bit-Codeeinheiten vertreten, ugs. "Ersatzzeichenpaar" (surrogate pair). Ebenso enthalten einige ANSI-Codepages (ugs. Doppelbyte-Zeichensätze) einige Doppelbyte-Zeichen. Aus praktischen Gründen werden diese jedoch fast immer als zwei einzelne Einheiten behandelt (der Einfachheit halber als "Zeichen" bezeichnet).

Puffer

Achten Sie bei der Reservierung eines Pufferspeichers via Buffer darauf, dass Sie die korrekte Anzahl von Bytes für die jeweils benötigte Kodierung angeben. Zum Beispiel:

ansi_buf  := Buffer(Kapazität_in_Zeichen)
utf16_buf := Buffer(Kapazität_in_Zeichen * 2)

Wenn eine ANSI- oder UTF-8-Zeichenkette mit StrPut in den Pufferspeicher geschrieben wird, sollten Sie keinesfalls StrLen zur Bestimmung der Puffergröße verwenden, da die ANSI- oder UTF-8-Länge von der nativen Länge (UTF-16) abweichen kann. Verwenden Sie stattdessen StrPut, um die erforderliche Puffergröße zu berechnen. Zum Beispiel:

required_bytes := StrPut(source_string, "cp0")
ansi_buf := Buffer(required_bytes)
StrPut(source_string, ansi_buf)

DllCall

Verwendet man den "Str"-Typ, handelt es sich um eine Zeichenkette im nativen Format des aktuellen Builds. Da einige Funktionen möglicherweise Zeichenketten in einem bestimmten Format benötigen oder zurückgeben müssen, stehen folgende Zeichenkettentypen zur Verfügung:

 ZeichengrößeC / Win32-TypenKodierung
WStr16-Bitwchar_t*, WCHAR*, LPWSTR, LPCWSTRUTF-16
AStr8-Bitchar*, CHAR*, LPSTR, LPCSTRANSI (die Standard-ANSI-Codepage des Systems)
Str--TCHAR*, LPTSTR, LPCTSTRÄquivalent zu WStr in AutoHotkey v2.

Wenn "Str" oder "WStr" für einen Parameter verwendet wird, wird die Adresse der Zeichenkette an die Funktion übergeben. Bei "AStr" wird eine temporäre ANSI-Kopie der Zeichenkette erstellt und die Adresse dieser Kopie übergeben. Generell sollte "AStr" nicht für einen Ausgabeparameter verwendet werden, da der Pufferspeicher gerade groß genug für die Eingabezeichenkette ist.

Hinweis: "AStr" und "WStr" können sowohl für die Parameter als auch für den Rückgabewert der Funktion verwendet werden.

Wenn ein Skript eine Funktion via DllCall aufruft, die eine Zeichenkette als Parameter akzeptiert, muss eine oder mehr der folgenden Vorgehensweisen verwendet werden:

  1. Wenn sowohl die Unicode- (W) als auch ANSI-Version (A) der Funktion verfügbar sind, lassen Sie das Suffix W oder A weg und verwenden Sie den "Str"-Typ für die Eingabeparameter oder den Rückgabewert. Zum Beispiel wird die DeleteFile-Funktion von kernel32.dll als DeleteFileA und DeleteFileW exportiert. Da DeleteFile selbst nicht wirklich existiert, versucht DllCall automatisch DeleteFileW:
    DllCall("DeleteFile", "Ptr", StrPtr(DateiName))
    DllCall("DeleteFile", "Str", DateiName)

    In beiden Fällen wird die Adresse der originalen, unveränderten Zeichenkette an die Funktion übergeben.

    In einigen Fällen kann dieser Ansatz nach hinten losgehen, da DllCall nur dann das Suffix W hinzufügt, wenn keine Funktion mit dem originalen Namen gefunden werden konnte. Zum Beispiel exportiert shell32.dll ExtractIconExW, ExtractIconExA und ExtractIconEx ohne Suffix, wobei die letzten beiden äquivalent sind. In diesem Fall bewirkt das Weglassen des Suffixes W, dass die ANSI-Version aufgerufen wird.

  2. Wenn die Funktion einen bestimmten Zeichenkettentyp als Eingabevariable akzeptiert, kann das Skript den passenden Zeichenkettentyp verwenden:
    DllCall("DeleteFileA", "AStr", DateiName)
    DllCall("DeleteFileW", "WStr", DateiName)
  3. Wenn die Funktion einen Zeichenkettenparameter hat, der für die Ausgabe verwendet wird, muss das Skript wie oben beschrieben Pufferspeicher reservieren und diesen an die Funktion übergeben. Wenn der Parameter Eingabe akzeptiert, muss das Skript auch die Eingabezeichenkette in das passende Format umwandeln; verwenden Sie dazu StrPut.

NumPut / NumGet

Wenn NumPut oder NumGet mit Zeichenketten verwendet werden, muss der Offset und Typ für den angegebenen Zeichenkettentyp korrekt sein. Folgendes kann Ihnen dabei helfen:

;  8-bit/ANSI   strings:  size_of_char=1  type_of_char="UChar"
; 16-Bit/UTF-16 Zeichenketten:  size_of_char=2  type_of_char="UShort"
nth_char := NumGet(buffer_or_address, (n-1)*size_of_char, type_of_char)
NumPut(type_of_char, nth_char, buffer_or_address, (n-1)*size_of_char)

Für das erste Zeichen sollte n den Wert 1 haben.

Pointer-Größe

Pointer sind in 32-Bit-Builds 4 Bytes und in 64-Bit-Builds 8 Bytes groß. Skripte, die Strukturen oder DllCalls verwenden, sollten das berücksichtigen, um eine korrekte Ausführung auf beiden Plattformen zu gewährleisten. Bestimmte Bereiche, die auch betroffen sind:

Für Größen- und Offset-Berechnungen kann A_PtrSize verwendet werden. Für DllCall, NumPut und NumGet kann je nach Bedarf der Ptr-Typ verwendet werden.

Beachten Sie, dass der Offset eines Feldes üblicherweise die Gesamtgröße aller vorherigen Felder ist. Beachten Sie auch, dass Handles (einschließlich Typen wie HWND und HBITMAP) prinzipiell Pointer-Typen sind.

/*
  typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;    // Ptr
    HANDLE hThread;
    DWORD  dwProcessId; // UInt (4 Bytes)
    DWORD  dwThreadId;
  } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
*/
pi := Buffer(A_PtrSize*2 + 8) ; Ptr + Ptr + UInt + UInt
DllCall("CreateProcess", <gekürzt>, "Ptr", &pi, <gekürzt>)
hProcess    := NumGet(pi, 0)         ; Standardmäßig "Ptr".
hThread     := NumGet(pi, A_PtrSize) ;
dwProcessId := NumGet(pi, A_PtrSize*2,     "UInt")
dwProcessId := NumGet(pi, A_PtrSize*2 + 4, "UInt")