DllCall()

提供: AutoHotkey Wiki
移動: 案内検索

実行制御 | GUI表示 | 演算・変数 | メモリ・DLL操作 | 文字列操作 | キーボード | マウス | シェル | ウィンドウ | ウィンドウグループ
ステータスバー | コントロール | サウンド | ファイル | INIファイル | レジストリ | 環境変数 | AutoHotkey | その他 | 設定関係 | オブジェクト

DllCall()[編集]

任意のDLLの関数を呼び出して返り値を得る。

Result := DllCall("[DllFile]\Function" [, Type1, Arg1,..., TypeN, ArgN [, "[Cdecl ]ReturnType"]])

Parameters[編集]

引数名 説明
Result DllCallによって呼び出す関数が返す値。関数が戻り値を返さない場合は、符号無し整数となる。
何らかのエラーによって関数のコールが成立しない場合は、空白文字列が返される。
[DllFile]\Function DLLのファイル名と関数名。を \で区切って記述。ディレクトリパスを省略した場合は以下の順序でDLLを検索する。
  1. カレントディレクトリ(A_WorkingDir)
  2. AutoHotkey.exeのあるディレクトリ
  3. およびシステムフォルダなどPATHが通っているディレクトリ

ファイル名の .dllは省略可能。(例: "kernel32\GetCommandLineA")

User32.dll、Kernel32.dll、ComCtl32.dll、Gdi32.dll内の関数の場合、DllFileを省略することができる。
また、この場合、関数名の末尾に Aがつく関数名は、 Aを省略して書くことができる。
たとえば、 "kernel32\GetCommandLineA"は "GetCommandLine"と書いても同じ結果になる。

1.0.46.08~: 文字列の代わりに数値を指定した場合、関数のアドレスを指定したものと見なす。
(GetProcAddressなどで取得した関数のアドレスを指定することも出来る)。
なお、これは RegisterCallback や COM も含まれる。

AHKL 関数名が見つからない場合、ANSI版では従来通りAを、ユニコード版ではWを付与して関数名を再検索する。この機能は上記のウィンドウズのコアDLLを含み全てのDLLについて動作する。詳しくはAutoHotkey_Lの互換性を参照。

Type1, Arg1,...,
TypeN, ArgN
引数の型と引数として与えるデータの組。Type1,Type2,...には、後述する型名を指定する。
Typeは "で囲んでも囲まなくてもよい。空白や *を含むときは必ず囲む。
Arg1,Arg2,...には、引数として与えるデータを指定。
式を指定することも可能。
これらの組は、いくつでも指定可能。
"[Cdecl ]ReturnType" 返り値の型を後述の型名から指定する。
4バイト符号つき整数もしくはBOOL値の場合、省略してかまわない。

DLLの呼び出し規約が一般的なStdCall方式ではなくcdecl方式の場合、型名の前に半角スペースで区切って Cdeclと書く必要がある。

返り値[編集]

DLLの関数が返した値。
DLLの関数が値を返さない場合、返り値は内容不定な整数値になる。
関数の呼び出しに失敗した場合、返り値は空になる。

型名[編集]

型名 説明
Str 引数を文字列として与える。
実際には、文字列の格納されたメモリ領域のアドレスが送られる。
対応するArgNに式でなく変数名を指定した場合( Array%A_Index%のような指定も含む)、変数自体のアドレスが送られる。
この場合、DLLの関数が文字列の内容を変更した場合、変数の内容が変更される。(例: DllCall("CharUpper", "str", VarName))

DLL関数側で操作するためにあらかじめ大きなメモリ領域を確保しておく必要がある場合、 VarSetCapacity(VarName,NewLength)として変数のメモリサイズを明示的に指定しておく必要がある。

str *とすると、 文字列の格納されたメモリ領域のアドレスが格納されたメモリ領域のアドレスが送られるようになる。

AStr/WStr AHKL ASCII文字列/ユニコード(Wide)文字列。詳しくはAutoHotkey_Lの互換性を参照。
Int64 64ビット整数(符号の有無については Uプレフィクスの欄を参照)
Int 32ビット整数(符号の有無については Uプレフィクスの欄を参照)
Short 16ビット整数(符号の有無については Uプレフィクスの欄を参照)
Char 8ビット整数(符号の有無については Uプレフィクスの欄を参照)
Float 32ビット浮動小数点値
Double 64ビット浮動小数点値
Ptr AHKL ポインタで利用する整数型。Ptr は配列や構造体(例 RECT *)、もしくはハンドル(例 HWND, HBRUSH, HBITMAP)のポインタとしてのみ利用すべきである。値のポインタ、例えば LPDWORD や int*、に対しては通常利用すべきではない、というのも * もしくは P サフィックスを利用することで該当値のやりとりが出来るためである(例は後述)。

Ptr は他の型と同様に * もしくは Pサフィックスが適応できる、これは LPVOID* やそれに類似した型に対して利用可能である。

UPtr も有効であるが、AutoHotkeyは符号無し64ビットはサポートされないため、32ビット版においては符号なしのみとなっている。

過去のバージョンのAutoHotkeyとの互換性を考えた場合、以下のようにすることで解決が出来る:

Ptr := A_PtrSize ? "Ptr" : "UInt"	; If A_PtrSize is not defined, use UInt instead.
DllCall("DeleteFile", Ptr, &filename)	; Omit the quote marks around Ptr.

備考: NULL値のポインタを渡したい場合は、0 を引き渡すとよい。

Pサフィクス 型名の後に Pもしくは *をつけると、データが格納されたメモリ領域のアドレスをやり取りするようになる。(例: IntP)
対応するArgNに式でなく変数名を指定した場合( Array%A_Index%のような指定も含む)、DLL関数内でのメモリ内容の変更がDLL呼出し後の変数の内容に反映される。
Uプレフィクス UInt UShort UCharの前につけて、符号無し(unsigned)整数として扱うことを指定する。
符号なしの整数では、負の数が扱えない代わりに扱える正の数の範囲が2倍になる。
通常の引数では、Uをつけなくても自動判別される。(符号つき整数の範囲外の正の数は符号無し整数として扱われる)
*サフィクス付きの引数と、ReturnTypeでは必ず指定する必要がある。

符号無し整数型の引数に負の値が指定された場合、符号つき整数における同様のビット列を符合無し整数としてそのまま送る。
たとえば、 UInt型に -1が指定された場合、 0xFFFFFFFFが送られる。これは符号無し整数では 4294967295である。 UInt64の実装は不完全である。
ReturnTypeとして Int64を指定していても、負の値(例: -1)が返ってきたときに最上位ビットが1の巨大な整数(例: 0xFFFFFFFFFFFFFFFF)とみなされてしまう。

ErrorLevel[編集]

AHKL [v1.1.04+] この関数は失敗した場合に例外をスローすることができる。詳細は実行時エラーを参照。

0 関数呼び出しは成功した
-1 [DllFile]\Functionが数値になっていた(文字列である必要がある)
-2 引数の型が間違っている(Int型の引数に文字列を渡した場合など)
-3 DllFileで指定したDLLファイルが存在しないかアクセスに失敗した。DLLFileにパスが設定されていない場合、DLLがパスの通ったシステムディレクトリか、A_WorkingDirにある必要がある。ファイルへのアクセス権が無かったり、AutoHotkey 32bitから64bitのDLLの関数を呼び出そうとした(またはその逆で64bitのAutoHotkeyから32bitのDLLの関数を呼び出し)場合もこのエラーが出る。
-4 DLLは見つかったが、Functionで指定した関数が無かった
1以上の
整数
致命的エラーで関数呼び出しが不正終了した。
この場合、ErrorLevelはエラーコードになる。
不正終了の場合、返り値は空になるが、 *付きの型の引数に指定した変数の内容は変更されている場合がある。
An
(n は整数)
引数の数が合わなかった。
n は実際に送られた引数の合計バイト数と、正しい合計バイト数の差。
n が正の場合引数の数が多すぎ、n が負の場合引数の数が少なすぎたことを示す。

例外 と A_LastError[編集]

AutoHotkeyには組み込みの例外ハンドラがあるが、依然としてDllCallを利用することによるクラッシュを十分に防ぐことはできない。これは、関数の例外よるもではなく、不正なポインタやヌル終端の無い文字列といった不適切な事象によって引き起こされる。もしスクリプトによって、不正なポインタや十分な容量のない"Str"のような不適切な引数を引き渡した場合は、何ら関数に落ち度はない。また、"Int" と "IntP" を混同するといった、不適切な引数型や戻り値型を指定することによって、スクリプトは容易にクラッシュしてしまう。

DLLの呼び出し後のエラーを判定するには GetLastError関数 をコールした内容を検査するが、組み込み変数の A_LastError を参照することで、同内容の結果を得ることが出来る。A_LastError は 0 ~ 4294967295 の整数(常に10進数フォーマット)であり、ErrorLevel と同様にスレッド毎に値が保持される。なお、A_LastErrorRun/RunWait でもセットされる。

構造体と配列の扱い[編集]

AutoHotkeyの機能としては、構造体や配列は用意されていない。
ただし、通常の変数を文字列としてではなく、バイト列を格納するバッファとして使用することで、構造体や配列を扱うDLL関数も利用することができる。

バッファとして使用したい変数のサイズは、使用する前に VarSetCapacity() 関数で設定しておく。
DLL関数に引数として渡すときは該当変数のアドレスをポインタ型(UInt もしくは Ptr)で渡す。
構造体のメンバ・配列の要素の値を設定・取得するには、NumPut(), NumGet() を使用する。

VarSetCapacity(point, 8)
DllCall("GetCursorPos", "UInt", &point, "Int") ; 赤字はポインタなのでAHKLの場合は Ptr にする
x := NumGet(point, 0, "Int")
y := NumGet(point, 4, "Int")
MsgBox, %x%`,%y%

既知の制限事項[編集]

変数のアドレス(例 &MyVar )を関数に引き渡し、関数側で変数内容の長さを変更した場合は、変数の使用領域が不正になる場合がある。これを回避するにはいかのいずれかの方法をおこなう。

  • ポインタ渡しを行わず "Str" 型にする
  • DLLの呼び出し後に VarSetCapaciry(MyVar, -1) とすることで変数内の使用領域の再計算を行わせる

DLLで呼び出す関数によって、バイナリ値で 0 を含むデータが生成された場合、0 移行の値は不可視となってしまい、多くのコマンドや関数ではアクセス出来なくなってしまう。しかしながら、そのようなデータであってもアドレス演算子やNumPut/NumGet、DllCallではきちんと扱う事は可能である。

引数として文字列(のアドレス)を受け取り、文字列(のアドレス)を返すような関数を呼び出した場合、期待していたものとは異なるアドレスを返してくる可能性がある。たとえば普通のプログラム言語を用いて、CharLower(CharUpper(MyVar)) のようにした場合は、正常に小文字に変換されたものが返ってくる。しかし、同様のことを DllCall() で行った場合、MyVar の内容は大文字となっている。というのも、CharLowerが呼ばれる段階では、引き渡された文字列(のアドレス)は MyVar の内容とは異なる一時的な文字列を扱うからである。

MyVar = ABC
result := DllCall("CharLower", "Str", DllCall("CharUpper", "Str", MyVar, "Str"), "Str")

この例では、下線を引いた二つの "Str" のを "Ptr" にすることで正常に動作させることができる。こうすることで、CharUpper の返す値を純粋なアドレスとして取り扱い CharLower に引き渡す事ができるからである。

上記以外にも文字列を扱う上では様々な制約が発生してくることが考えられる。詳しくはスクリプトの互換性を参照のこと。

パフォーマンス改善[編集]

DllCallでは、関数実行のたびにDLLをロードして、関数終了後に開放している。 同じ関数を短時間に頻繁に呼び出す場合、下記のようにしてDLLをロードする処理を自前で記述しておくことで、パフォーマンスを改善することができる。 なお、User32.dll、Kernel32.dll、ComCtl32.dll、Gdi32.dllの各DLLについては、常にロードされたままになっているため、この処理は必要ない。

hModule := DllCall("LoadLibrary", str, "MyFunctions.dll")  ;ロードする
;MyFunctions.dllの関数を使用する処理
DllCall("FreeLibrary", UInt, hModule)  ;開放する

DLL確保/開放の手動化[編集]

DLLの処理によっては、処理の途中でDLLが開放してしまうと正常に動作しない場合がある。
その場合、上記の例のように手動でLoadLibrary/FreeLibraryを行うことで、本来の動作になることがある。

Remarks[編集]

Windowsに標準で存在するDLLの関数については、MSDN Library、簡潔なものとしては、Win32 API関数リストというページもある。
その他にもウィンドウメッセージ一覧で検索 すると解説しているサイトが多数見つかる。

上記のような関数リファレンスでは、引数や返り値の型にAutoHotkeyで使用できるよりも多くの種類がある。
おおむね以下のような対応になっている。こちら以外はデータ型対応表を参照。

リファレンスでの型名 AutoHotkeyでの型名
BOOL
BOOLEAN
Int (0のときが偽、1のときが真)
BYTE UChar
char Char
char ** StrP
WCHAR Short
LPSTR * StrP
LPTSTR Str
WORD UShort
DWORD UInt
LONG Int
LONGLONG Int64
HWND
HANDLE
先頭にHが付くもの
UInt / AHKL Ptr
LRESULT Int / AHKL Ptr
WPARAM
LPARAM
UInt / AHKL UPtr
LUID Int64
IPADDR UInt
LANGID UShort
LCID UInt
COLORREF UInt (24ビットの色を 0x00BBGGRR 形式で格納)
先頭に LP が付くもの
先頭に P が付くもの
Pサフィクスをつける

文字列や構造体としてnullを送りたい場合は、型をInt、内容を0にすればよい。

色々なところで使用するDLL関数は、いちいちDllCall()の呼び出しを記述するより、以下のようなAutoHotkeyの関数として宣言しておくと便利

GetParent(hwnd) {
  Return DllCall("GetParent", "Ptr", hwnd, "Ptr")
}

DllCallでは引数にStr型を指定した場合、やり取りされる値を一時的に別の場所にコピーしてからやり取りする。
そのため、関数呼び出しを入れ子にした場合などにほかの言語と違った動作になる場合がある。
その場合、関すの返り値として与えられる文字列バッファへのポインタを UInt型で受け取り、 UInt型でほかのDLL関数に渡すようにすると、途中で勝手にコピーされることがなくなり、期待した動作にできる。

Related[編集]

VarSetCapacity(), NumPut(), NumGet(), RegisterCallback(), 関数, PostMessage, SysGet

Example(s)[編集]

; Example: Call the Windows API function "MessageBox" and report which button the user presses.
WhichButton := DllCall("MessageBox", "int", "0", "str", "Press Yes or No", "str", "Title of box", "int", 4)
MsgBox You pressed button #%WhichButton%.
; Example: Call the API function "IsWindowVisible" to find out if a Notepad window is visible.
DetectHiddenWindows On
If (not DllCall("IsWindowVisible", "UInt", WinExist("Untitled - Notepad")))  ; WinExist() returns an HWND.
  MsgBox The window is not visible.

; Example: Call the API's wsprintf() to pad the number 432 with leading zeros to make it 10 characters wide.
VarSetCapacity(ZeroPaddedNumber, 20)  ; Ensure the variable is large enough to accept the new string.
DllCall("wsprintf", "str", ZeroPaddedNumber, "str", "%010d", "int", 432, "Cdecl")  ; Requires the Cdecl calling convention.
MsgBox %ZeroPaddedNumber%

; Example: QueryPerformanceCounter() can be used if you need more precision than A_TickCount's 10ms.
If DllCall("QueryPerformanceCounter", "Int64 *", Counter)
    MsgBox Current counter value: %Counter%
Else
    MsgBox System doesn't support counter.
; Example: When passed a window's Unique ID and the text or ClassNN of one of its controls,
; the following function returns the HWND (unique ID) of that control.

GetChildHWND(ParentHWND, ChildClassNN)
{
  WinGetPos, ParentX, ParentY,,, ahk_id %ParentHWND%
  If ParentX =
    Return  ; Parent window not found (possibly due to DetectHiddenWindows).
  ControlGetPos, ChildX, ChildY,,, %ChildClassNN%, ahk_id %ParentHWND%
  If ChildX =
    Return  ; Child window not found, so return a blank value.
  ; Convert child coordinates -- which are relative to its parent's upper left
  ; corner -- to absolute/screen coordinates for use with WindowFromPoint().
  ; The following INTENTIONALLY passes too many args to the function because
  ; each arg is 32-bit, which allows the function to automatically combine
  ; them into one 64-bit arg (namely the POINT structure):
  Return DllCall("WindowFromPoint", "int", ChildX + ParentX, "int", ChildY + ParentY)
}
; The following example requires that the GetChildHWND() function above also be present.
; This example monitors the active window and displays the vertical scroll bar position
; of its focused control in real time.
#Persistent
SetTimer, WatchScrollBar, 100
Return
WatchScrollBar:
ActiveWindow := WinExist("A")
If not ActiveWindow	; No active window.
  Return
ControlGetFocus, FocusedControl, ahk_id %ActiveWindow%
If not FocusedControl	; No focused control.
  Return
; Display the vertical or horizontal scroll bar's position in a ToolTip:
ChildHWND := GetChildHWND(ActiveWindow, FocusedControl)
ToolTip % DllCall("GetScrollPos", "UInt", ChildHWND, "Int", 1)  ;  Last param is 1 for SB_VERT, 0 for SB_HORZ.
Return
; Example: Write some text to a file then read it back into memory (requires v1.0.34+). This method can be
; used to help performance in cases where multiple files are being read or written simultaneously.
FileSelectFile, FileName, S16,, Create a new file:
If FileName =
  Return
GENERIC_WRITE = 0x40000000	; Open the file for writing rather than reading.
CREATE_ALWAYS = 2		; Create new file (overwriting any existing file).
hFile := DllCall("CreateFile", str, FileName, Uint, GENERIC_WRITE, Uint, 0, UInt, 0, UInt, CREATE_ALWAYS, Uint, 0, UInt, 0)
If not hFile
{
  MsgBox Can't open "%FileName%" for writing.
  Return
}
TestString = This is a test string.
DllCall("WriteFile", UInt, hFile, str, TestString, UInt, StrLen(TestString), UIntP, BytesActuallyWritten, UInt, 0)
DllCall("CloseHandle", UInt, hFile)  ; Close the file.
; Now that the file was written, read its contents back into memory.
GENERIC_READ = 0x80000000	; Open the file for reading rather than writing.
OPEN_EXISTING = 3		; This mode indicates that the file to be opened must already exist.
FILE_SHARE_READ = 0x1		; Whether other processes can open the file while we have it open.
FILE_SHARE_WRITE = 0x2
hFile := DllCall("CreateFile", str, FileName, UInt, GENERIC_READ, UInt, FILE_SHARE_READ|FILE_SHARE_WRITE, UInt, 0, UInt, OPEN_EXISTING, Uint, 0, UInt, 0)
If not hFile
{
  MsgBox Can't open "%FileName%" for reading.
  Return
}
; Make the variable empty for testing purposes, but ensure it retains sufficient capacity:
BytesToRead := VarSetCapacity(TestString, StrLen(TestString))
DllCall("ReadFile", UInt, hFile, str, TestString, UInt, BytesToRead, UIntP, BytesActuallyRead, UInt, 0)
DllCall("CloseHandle", UInt, hFile)  ; Close the file.
MsgBox The following string was read from the file: "%TestString%"
; Structure Example: Pass the address of a RECT structure to GetWindowRect(), which sets the structure's
; members to the positions of the left, top, right, and bottom sides of a window (relative to the screen).
Run Notepad
WinWait Untitled - Notepad	; This also sets the "Last Found Window" for use with WinExist() below.
VarSetCapacity(Rect, 16)	; A RECT is a struct consisting of four 32-bit integers (i.e. 4*4=16).
DllCall("GetWindowRect", UInt, WinExist(), Str, Rect)  ; WinExist() returns an HWND.
MsgBox % "Left " . ExtractInteger(Rect, 0, true) . " Top " . ExtractInteger(Rect, 4, true)
  . " Right " . ExtractInteger(Rect, 8, true) . " Bottom " . ExtractInteger(Rect, 12, true)
; Structure Example: Pass to FillRect() the address of a RECT structure that indicates a part of the
; screen to temporarily paint red.
VarSetCapacity(Rect, 16, 0)	; Set capacity to hold four 4-byte integers and initialize them all to zero.
InsertInteger(A_ScreenWidth//2, Rect, 8)	; The third integer in the structure is "rect.right".
InsertInteger(A_ScreenHeight//2, Rect, 12)	; The fourth integer in the structure is "rect.bottom".
hDC := DllCall("GetDC", UInt, 0)			; Pass zero to get the desktop's device context.
hBrush := DllCall("CreateSolidBrush", UInt, 0x0000FF)	; Create a red brush (0x0000FF is in BGR format).
DllCall("FillRect", UInt, hDC, Str, Rect, UInt, hBrush)  ; Fill the specified rectangle using the brush above.
DllCall("ReleaseDC", UInt, 0, UInt, hDC)	; Clean-up.
DllCall("DeleteObject", UInt, hBrush)		; Clean-up.