DllCall()

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

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

Parameters

引数名説明
[DllFile]\Function DLLのファイル名と関数名。を「\」で区切って記述。
ディレクトリパスを省略した場合、システムフォルダなどPATHが通っているディレクトリと、A_WorkingDir内の該当するDLLが検索される。
ファイル名の「.dll」は省略可能。(例:「"kernel32\GetCommandLineA"」)

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

文字列の代わりに「GetProcAddress」などで取得した関数のアドレスを指定することも出来る。
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 *」とすると、「文字列の格納されたメモリ領域のアドレス」が格納されたメモリ領域のアドレスが送られるようになる。
Int64 64ビット整数(符号の有無については「U」プレフィクスの欄を参照)
Int 32ビット整数(符号の有無については「U」プレフィクスの欄を参照)
Short 16ビット整数(符号の有無については「U」プレフィクスの欄を参照)
Char 8ビット整数(符号の有無については「U」プレフィクスの欄を参照)
Float 32ビット浮動小数点値
Double 64ビット浮動小数点値
「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

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

Remarks

Windowsに標準で存在するDLLの関数については、MSDN LibraryWinAPI Database for VB Programmerなどが詳しい。
簡潔なものとしては、Win32 API関数リストというページもある。

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

リファレンスでの型名AutoHotkeyでの型名
BOOLInt (0のときが偽、1のときが真)
BOOLEANInt (0のときが偽、1のときが真)
BYTEUChar
char **StrP
COLORREFUInt(24ビットの色を0xBBGGRR形式で格納)
DWORDUInt
HANDLEUInt
先頭にHが付くものUInt
IPADDRUInt
LANGIDUShort
LCIDUInt
LONGInt
LONGLONGInt64
LPARAMInt
先頭にLPが付くもの「P」サフィクスをつける
LPSTR *StrP
LRESULTInt
LUIDInt64
先頭にPが付くもの「P」サフィクスをつける
WCHARShort
WORDUShort
WPARAMInt

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

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

getLastError()
{
	return DllCall("GetLastError")
}

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

構造体と配列の扱い

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

バッファとして使用したい変数のサイズは、使用する前にVerSetCapacity()関数で設定しておく。
DLL関数に引数として渡すときは、「Str」型として渡す。
構造体のメンバ・配列の要素の値を設定・取得するには、下記のような関数を使用する。

ExtractInteger(Value,Offset,IsSigned,Size)
Value:		値を取得するバッファ
Offset:		バッファの先頭からのオフセット(バイト数)
IsSigned:	返り値を符号つき整数として扱う場合trueにする
Size:		取り出す整数値のサイズ(バイト数)
ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
{
	SourceAddress := &pSource + pOffset  ; Get address and apply the caller's offset.
	result := 0  ; Init prior to accumulation in the loop.
	Loop %pSize%  ; For each byte in the integer:
	{
		result := result | (*SourceAddress << 8 * (A_Index - 1))  ; Build the integer from its bytes.
		SourceAddress += 1  ; Move on to the next byte.
	}
	if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
		return result  ; Signed vs. unsigned doesn't matter in these cases.
	; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
	return -(0xFFFFFFFF - result + 1)
}
InsertInteger(Value,Buffer,Offset,Size)
Value:	設定する値
Buffer:	設定先を含むバッファ
Offset:	バッファの先頭からのオフセット(バイト数)
Size:	整数値のサイズ(バイト数)
InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; To preserve any existing contents in pDest, only pSize number of bytes starting at pOffset
; are altered in it. The caller must ensure that pDest has sufficient capacity.
{
	mask := 0xFF  ; This serves to isolate each byte, one by one.
	Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
	{
		DllCall("RtlFillMemory", UInt, &pDest + pOffset + A_Index - 1, UInt, 1  ; Write one byte.
			, UChar, (pInteger & mask) >> 8 * (A_Index - 1))  ; This line is auto-merged with above at load-time.
		mask := mask << 8  ; Set it up for isolation of the next byte.
	}
}

パフォーマンス改善

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を行うことで、本来の動作になることがある。

Related

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

Examples


; 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.