バイナリ互換性

本書では、外部ライブラリとの連携や、コントロールやウィンドウへのメッセージ送信の際に重要となるトピックをいくつか紹介します。

ユニコードとANSIの比較

注:このセクションは、ドキュメントの他の部分でカバーされているトピックを構築しています:文字列文字列のエンコード

文字列(テキスト値)の中で、各文字の数値文字コードとサイズ(バイト数)は、文字列のエンコーディングに依存します。これらの詳細は、通常、次のいずれかを実行するスクリプトで重要です:

AutoHotkey v2はネイティブでUnicode(UTF-16)を使用しますが、一部の外部ライブラリやウィンドウメッセージではANSI文字列を必要とする場合があります。

ANSI:1文字が1バイト(8ビット)です。127以上の文字コードは、お使いのシステムの言語設定(または、ファイルへの書き込みなど、テキストをエンコードする際に選択されたコードページ)に依存します。

Unicode:各文字は2バイト(16ビット)です。文字コードは、UTF-16形式で定義されたものです。

意味:技術的には、一部のUnicode文字は2つの16ビットコードユニットで表され、集合的に "サロゲートペア "として知られています。同様に、いくつかのANSIコードページ(一般にダブルバイト文字セットとして知られている)には、いくつかのダブルバイト文字が含まれています。しかし、実用上の理由から、これらはほとんどの場合、2つの個別のユニット(簡単のために「文字」と呼ばれる)として扱われます。

Buffer

バッファを割り当てる際には、必要なエンコーディングに応じた正しいバイト数を計算するように注意してください。事例:

ansi_buf := Buffer(capacity_in_chars)
utf16_buf := Buffer(capacity_in_chars * 2)

StrPutでANSIまたはUTF-8の文字列をバッファに書き込む場合、ANSIまたはUTF-8の長さがネイティブ(UTF-16)の長さと異なる場合があるので、バッファのサイズを決めるのにStrLenを使用しないでください。代わりに、StrPutを使用して必要なバッファサイズを計算します。事例:

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

DllCall

"Str"型が使用される場合、現在のビルドのネイティブフォーマットの文字列を意味します。関数によっては、特定の形式の文字列を要求したり、返したりすることがあるため、次のような文字列型を用意しています:

 文字サイズC / Win32 Typesエンコード
WStr16-bitwchar_t*、WCHAR*、LPWSTR、LPCWSTRUTF-16
AStr8-bitchar*、CHAR*、LPSTR、LPCSTRANSI(システムデフォルトのANSIコードページ)
Str--TCHAR*、LPTSTR、LPCTSTRAutoHotkey v2 のWStrに相当します。

パラメータに "Str" または "WStr" を使用した場合、文字列のアドレスが関数に渡されます。"AStr"の場合、文字列の一時的なANSIコピーが作成され、そのアドレスが代わりに渡されます。バッファは入力文字列を保持するのに十分な大きさしかないため、原則として "AStr"を出力パラメータに使用するべきではありません。

注:"AStr"および "WStr"は、パラメータおよび関数の戻り値に対して同様に有効です。

一般に、スクリプトがDllCallを介して、文字列をパラメータとして受け付ける関数を呼び出す場合、以下のアプローチのうちの1つ以上を取る必要があります:

  1. Unicode(W)版とANSI(A)版の両方が利用可能な場合、WまたはAの接尾辞を省略し、入力パラメータまたは戻り値に "Str"型を使用します。例えば、DeleteFile関数は、DeleteFileADeleteFileWとしてkernel32.dllからエクスポートされます。DeleteFile自体は実際には存在しないので、DllCallは自動的にDeleteFileWを試行します:
    DllCall("DeleteFile", "Ptr", StrPtr(filename))
    DllCall("DeleteFile", "Str", filename)

    どちらの場合も、修正されていない元の文字列のアドレスが関数に渡されます。

    DllCallは、元の名前の関数が見つからなかった場合にのみ、Wサフィックスを追加するため、この方法が裏目に出る場合もあります。例えば、shell32.dllはExtractIconExW、ExtractIconExA、ExtractIconExをサフィックスなしでエクスポートし、最後の2つは同等です。その場合、Wサフィックスを省略すると、ANSIバージョンが呼び出されることになります。

  2. 関数が入力として特定のタイプの文字列を受け入れる場合、スクリプトは適切な文字列タイプを使用することができます:
    DllCall("DeleteFileA", "AStr", filename)
    DllCall("DeleteFileW", "WStr", filename)
  3. 関数に出力用の文字列パラメータがある場合、スクリプトは上記のようにバッファを確保し、それを関数に渡す必要があります。パラメータが入力を受け付ける場合、スクリプトは入力文字列を適切な形式に変換する必要があります。このためにStrPutを使用することができます。

NumPut / NumGet

NumPutまたはNumGetが文字列と共に使用される場合、オフセットと型は与えられた文字列の型に対して正しくなければならない。以下は目安としてお使いください:

;  8-bit/ANSI   strings:  size_of_char=1  type_of_char="UChar"
; 16-bit/UTF-16 strings:  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)

最初の文字については、nは値1でなければならない。

ポインタサイズ

ポインターは32ビットビルドでは4バイト、64ビットビルドでは8バイトです。構造体やDllCallを使用するスクリプトは、両方のプラットフォームで正しく実行するために、これを考慮する必要があります。影響を受ける具体的な地域は以下の通りです:

サイズとオフセットの計算には、A_PtrSizeを使用します。DllCall、NumPut、NumGetについては、適宜Ptr型を使用します。

フィールドのオフセットは、通常、そのフィールドに先行するすべてのフィールドの合計サイズであることを忘れないでください。また、ハンドル(HWNDやHBITMAPなどの型も含む)は基本的にポインタ型であることに注意してください。

/*
  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", <omitted for brevity>, "Ptr", &pi, <omitted>)
hProcess    := NumGet(pi, 0)         ; デフォルトは "Ptr"です。
hThread     := NumGet(pi, A_PtrSize) ;
dwProcessId := NumGet(pi, A_PtrSize*2,     "UInt")
dwProcessId := NumGet(pi, A_PtrSize*2 + 4, "UInt")