スクリプトの互換性

提供: AutoHotkey Wiki
移動: 案内検索
リファレンス > AutoHotkey_Lとは > スクリプトの互換性 - 公式ドキュメント

AutoHotkey_Lはリビジョン42からユニコードサポートを行うようになった。
ユニコードへの対応のため、従来のANSI版とは別にユニコード版が派生して別ビルドとなっている。
ANSI版(または本家版)用に組まれたスクリプトをユニコード版で利用する場合、いくつかの互換性の問題が生じる。
本家版用に組まれたスクリプトに対する非互換の問題も同時に触れる。

ANSI版とユニコード版[編集]

AutoHotkey_LにはANSI版とユニコード版の二つのリリースビルドが存在する。
前者は本家AutoHotkeyに独自の拡張機能を追加したもので、既存のスクリプトとの互換性は概ね高い。
一方、後者は文字列の取り扱いを全てユニコードとして取り扱うように変更した上で、独自の拡張機能を追加したものとなっている。

スクリプトの文法面で両者に全く違いはないが、文字列を取り扱う部分の挙動面でANSIとユニコードで若干の違いが存在する。

版名 スクリプトの
エンコーディング
文字列の取り扱い 本家用スクリプトとの互換性
ユニコード版(W) UTF-8 ユニコード 中程度。文法面の不整合の修正に加え、文字列の取り扱いの修正が必要な場合がある。
駄目文字対策は不要。
ANSI版(A) ANSI(シフトJIS) ANSI 高い。文法面での不整合が無ければ動作する。

Unicode版推奨、日本語を含む文字列の取り扱いが断然便利な為。
  64ビットOSでAHKL32ビット版の使用は制約が多いので、64ビット版を強く推奨

上表の詳細は、次項以降を参照のこと。

スクリプトファイルのエンコーディング[編集]

ANSI版を利用する場合は、スクリプトは従来通りANSI(シフトJIS)で読み込まれる。

一方、ユニコード版を利用する場合、スクリプトファイルはデフォルトでUTF-8で読み込まれる
このため従来のANSI(シフトJIS)で記述していたスクリプトの日本語部分は化けてしまう。
従って、テキストエディタ(メモ帳でも可)等を利用してスクリプトファイルをUTF-8形式に変更してやる必要がある。

なお、AutoHotkey.exeに引き渡すコマンドライン引数の第1引数を "/CP932" とすることで読み込むファイルのエンコーディングをシフトJIS(CP932)として認識させることが出来る。
従って、「.ahk」ファイルの関連付けをフォルダオプションで変更することで自動的にシフトJISでスクリプトを実行させることは可能である。
関連付けの変更をレジストリファイルでやる場合は以下の通り。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AutoHotkeyScript\Shell\Open\command]
@="\"C:\\Program Files\\AutoHotkey\\AutoHotkey.exe\" /CP932 \"%1\" %*"

ただし上記の手法で起動したシフトJISのスクリプトを、トレイからReloadを選んだり、スクリプト内でReloadコマンドで再起動させた時は、このオプションが無視されてしまうので注意が必要である。

※ なお、ユニコード版では本家版であるような記述する時にエスケープが必要な文字は無く、逆に不要なので外す必要がある。

文字列の内部形式[編集]

  • ユニコード
文字列は UTF-16(LE)で格納される。どの文字も等しく2バイトで構成されるため、DllCallVarSetCapacity、NumPut、NumGet などにおいて文字列を扱う際に注意が必要である。
  • ANSI
文字列はウィンドウズデフォルトのANSIコードページで格納される。これは本家と同様である。文字1つを表現するためのバイト数はまちまちである。

どちらの場合でも文字は複数の8ビットや16ビットで表現されているが、パフォーマンスや簡便性において、それらは単なる文字の連続として扱われるという点に注意する必要がある。
なお、便宜上この文書のこれ以降では文字と表記されていものは、文字列形式がUTF-16であるかどうかに関わらず、単一の8ビットまたは16ビットで構成されているもととする。

VarSetCapacity[編集]

VarSetCapacity では指定変数の容量をバイト単位で指定する。
即ち、格納すべき文字数は、その内部形式に依存することになり、いくらかの計算をする必要が生じる:

VarSetCapacity(ansi_var,    size_in_chars)
VarSetCapacity(unicode_var, size_in_chars * 2)
VarSetCapacity(native_var,  size_in_chars * (A_IsUnicode ? 2 : 1))
VarSetCapacity(native_var, t_size(size_in_chars))  ; see below

VarSetCapacity は指定文字数を格納できるようにするため1文字分サイズを追加している。これはヌル終端の分である。
しかし、文字列の内部とは異なる形式の文字列を該当変数に格納する場合、size_in_chars はヌル終端を含ませるのが一般的である、

DllCall[編集]

データ型 "Str" を利用する場合、利用しているAutoHotkeyの内部形式に沿った文字列を指定しているものとみなす。
利用する関数によっては特定の文字列形式を要求する場合があるが、以下のようにすれば文字列形式を指定することができる。

型名 文字サイズ C言語 / Win32での型 文字コード
WStr 16ビット wchar_t*, WCHAR*, LPWSTR, LPCWSTR UTF-16
AStr 8ビット char*, CHAR*, LPSTR, LPCSTR ANSI (ウィンドウズのデフォルトコードページ)
Str (環境依存) TCHAR*, LPTSTR, LPCTSTR ユニコード版ではWStrと同等。ANSI版ではAStrと同等

"Str" もしくは現ビルドでの同等の型をパラメタとして利用した場合、 該当の文字列または変数のアドレスが関数に引き渡される。
それ以外の場合は、一時的に要求する型に変換して格納したのちに関数に引き渡される。
関数側では一時的にコピーされたバッファへと変更を加えるかも知れないが、変換後のバッファ長(ヌル終端が末端)を超えて書き込むことは出来ない。

備考: "AStr" と "WStr" の双方とも戻り値の型としても利用できる。

一般に、文字列を引数とする関数をDllCallを介して呼び出す場合、以下の中の何れかの方法でアプローチすることができる。

#関数にユニコード版(W)・ANSI版(A)の両方が存在する場合、それぞれの版に適した方をコールすることになる。
例を挙げると、"DeleteFile" 関数は内部的には "DeleteFileA" または "DeleteFileW" として知られている。即ち、"DeleteFile" という関数自体は存在しない。
AutoHotkey_Lは各版に対応した "A" または "W" 接尾辞を適宜付与した関数を探し出してコールを行う。

DllCall("DeleteFile", "Ptr", &filename)
DllCall("DeleteFile", "Str", filename)

上の例では、&filename によって文字列の先頭アドレスがそのまま(訳注:コード変換など無しに)渡されるので、関数側では "Str" 型と同じ形式で文字列を受けるものだと判断しなければならない。

備考: 指定する関数名が見つからない場合、AutoHotkey_L はいかなるDLLに対しても "A" または "W" の接尾辞を適宜付与するが、本家の AutoHotkey ではWindowsの中心モジュールである user32.dll、kernel32.dll、comctl32.dll および gdi32.dll に対してのみしか接尾辞の付与を行わない。
#関数が文字列の特定の形式しか受け付けない場合は、スクリプト側では適切な型を明示して引き渡す必要がある。

   DllCall("DeleteFileA", "AStr", filename)
   DllCall("DeleteFileW", "WStr", filename)

#関数側が引数の文字列に手を加える場合は、スクリプト側で上に示すように適切なバッファを用意してそのアドレスを関数に引き渡してやる必要がある。
仮に、内部形式とは異なる形式の文字列を引き渡すために、文字列型として AStrWStr を指定した場合、 AutoHotkey側では一時的なバッファを作成して変換した文字列を格納して関数に引き渡す。従って、関数が書き戻しを行うのはあくまでも一時バッファに対してになってしまう。
つまり、パラメタがDllCallによって上書きされるような場合は、スクリプト側で事前にパラメタ文字列を適切な形式になるようにバッファを確保して変換し、そのバッファアドレスを関数に引き渡してやる必要がある。バッファの確保は VarSetCapacty 、文字列の書き込みは StrPut を利用するとよい。

NumPut / NumGet[編集]

文字列に対して NumPutNumGet を利用する場合、そのオフセットと形式は文字列型に沿うものでなければならない。以下はその典型例:

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

変数 var がネイティブ形式で文字列を格納している場合、組み込み変数 A_IsUnicode を利用することで適切な値を決定することができる。

nth_char := NumGet(var, t_size(n-1), t_char())
NumPut(nth_char, var, t_size(n-1), t_char())

; Define functions for convenience and clarity:
t_char() {
    Return A_IsUnicode ? "UShort" : "Char"
}
t_size(char_count = 1) {
    Return A_IsUnicode ? char_count : char_count*2
}

ポインタのサイズ[編集]

(本家AutoHotkeyを含む)32ビット版ではポインタのサイズは4バイトであり、64ビット版では8バイトである。
構造体やDllCall等を利用するスクリプトが双方の環境できちんと動くようにするには、このことを十分に理解していなければならない。
影響を受ける箇所は以下の通り:

  • 構造体の各フィールドのオフセット計算
  • ポインタを持つ構造体のサイズ計算
  • DllCallNumPut/Get で利用するデータ型

サイズやオフセットの計算には組み込み変数の A_PtrSize を使う。 DllCallNumPut/Get のデータ型では Ptr を利用する。

通常、フィールドのオフセットはそれ以前のフィールドの総サイズになる。また、各種ハンドル(HWND、HBITMAP等)もまたポインタ型である。

/*
  typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;    // Ptr
    HANDLE hThread;
    DWORD  dwProcessId; // UInt (4 bytes)
    DWORD  dwThreadId;
  } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
*/
VarSetCapacity(pi, A_PtrSize*2 + 8) ; Ptr + Ptr + UInt + UInt
DllCall("CreateProcess", , "Ptr", &pi, )
hProcess    := NumGet(pi, 0)         ; Defaults to "Ptr".
hThread     := NumGet(pi, A_PtrSize) ; 
dwProcessId := NumGet(pi, A_PtrSize*2,     "UInt")
dwProcessId := NumGet(pi, A_PtrSize*2 + 4, "UInt")

その他の変更点[編集]

デフォルトスクリプト[編集]

スクリプトを指定せずに AutoHotkey_L を起動した場合、デフォルトスクリプトのファイル名は起動する実行ファイル(通常は AutoHotkey.exe)の拡張子を .ahk に変えたものとなる。

変数および関数の名称[編集]

[」、「]」および「?」は変数名としては利用できなくなった。結果として、「?」(三項演算子)では各オペランド間のスペースは不要となった。オブジェクト文法も参照のこと。

文法の検証[編集]

コマンド名はスペース・タブ・カンマのいずれかで終わらなければならない。本家の AutoHotkey とは異なり、「>」、「<」、「:」、「+」、「-」、「*」、「/」、「!」、「~」、「&」、「|」、「^」、「[」、「]」のいずれの文字もこの要求事項を満たすことはない。結果として、「MsgBox>foo」や「If!foo」のような記述方法はスクリプトの読み込み時にエラーとして検出され、決して「MsgBox,>foo」や「If !foo」といったような翻訳はなされない。

TransForm[編集]

ユニコード版の Transform のサブコマンドにはいくつか修正が加えられている。

  • Unicode は廃止となり ToCodePageFromCodePage に置き換わった。
  • HTML は機能追加された。

If var is[編集]

このコマンドでは「StringCaseSense, Locale」のようにコマンドで指定が無い限り、システムのロケールを無視する。

FileRead[編集]

FileRead では読み出し時にコードページ変換を行うことがある。対象ファイルがバイナリ形式だった場合、取得するデータは不正となりうる。
コードページ変換は以下のような特殊ケースでは働かないことがある:

  • オプション「*C」が指定されているとき: コードページおよび行末の変換は全く行われない。
  • オプション「*Pnnn」が指定されている時で、かつコードページ nnn が文字列の内部形式同等だった場合。
  • 現在のファイルエンコーディング(A_FileEncoding)がネイティブ形式だった場合。

FileOpen コマンドにはバイナリファイルを扱うのに適したメソッドがある。

SetFormat, Integer[Fast], H[編集]

大文字の「H」を指定した場合、16進数で出力される A~F の部分は大文字となる。本家版とはこれとは異なり常に小文字で出力される。

互換モード[編集]

スクリプトを起動するEXEファイルに対して互換モードを利用して Win95/98/Me または NT4 として動作するように設定した場合、スクリプトは正しく動作しない可能性がある。これは、互換モードは対象のアプリケーションに対して特定のウィンドウズのバージョンを詐称するのに対し、既にビルドが終わってしまった実行ファイルにはそれらのバージョンに対応する機能が削られているためである。例えば、OSのバージョンを Win95/98/Me の何れかにして互換モードで実行した場合、「MsgBox, %A_OSVersion%」と実行すると、「NT4」と表示される。

Run/RunWait[編集]

[L57+] カスタム動作 に加え、リビジョン57では Target に記述された内容に基づいて以下の動作をさせることが可能となった。

  • Targetが"?"で始まる場合、次の"?"が出現するまでの部分はアクションを指定したものとみなす。
  • 上記以外で、[文字列][半角スペース][実存するファイル、または実行可能ファイル(.exe;.bat;.com;.cmd;.hta)] と指定した場合は 文字列 部分はアクションを指定したものとみなす。これにより .ahk や .vbs 、.lnk のようなファイルタイプにパラメタを引き渡すことが可能となる、一方で wordpad.exe のような既知の実行ファイルは今までのバージョン通りにフルパスを必要とせずに起動可能である。