DllCall() [v1.0.33+]

Calls a function inside a DLL, such as a standard Windows API function.

Result := DllCall("[DllFile\]Function" [, Type1, Arg1, Type2, Arg2, "Cdecl ReturnType"])

 

Parameters

Result The actual value returned by the function. If the function is of a type that does not return a value, the result is an undefined integer. If the function cannot be called due to an error, the return value is blank (an empty string).
[DllFile\]Function

The DLL's file name followed by a backslash and the name of the function. For example: "MyDLL\MyFunction" (the file extension ".dll" is optional). If an absolute path isn't specified, DllFile is assumed to be in the system's PATH or A_WorkingDir.

DllFile may be omitted when calling a function that resides in User32.dll, Kernel32.dll, or ComCtl32.dll (or Gdi32.dll in v1.0.36.07+). For example, "User32\IsWindowVisible" produces the same result as "IsWindowVisible". For these standard DLLs, the letter "A" suffix that appears on some API functions may also be omitted. For example, "MessageBox" is the same as "MessageBoxA".

Performance can be dramatically improved when making repeated calls to a DLL by loading it beforehand.

Type1, Arg1 Each of these pairs represents a single parameter to be passed to the function. The number of pairs is unlimited. For Type, see the types table below. For Arg, specify the value to be passed to the function.
Cdecl ReturnType

The word Cdecl is normally omitted because most functions use the standard calling convention rather than the "C" calling convention (functions such as wsprintf that accept a varying number of arguments are one exception to this). If you omit Cdecl but the call yields ErrorLevel An -- where n is the total size of the arguments you passed -- Cdecl might be required.

If present, the word Cdecl should be listed before the return type (if any). Separate each word from the next with a space or tab. For example: "Cdecl Str"

ReturnType: If the function returns a 32-bit integer (Int), BOOL, or nothing at all, ReturnType may be omitted. Otherwise, specify one of the argument types from the types table below. The asterisk suffix is also supported.

 

Types of Arguments and Return Values

Type Description
Str

A string such as "Blue" or MyVar. If the called function modifies the string and the argument is a naked variable, its contents will be updated. For example, the following call would convert the contents of MyVar to uppercase: DllCall("CharUpper", "str", MyVar)

However, if the function is designed to store a string larger than a variable's current capacity, ensure that the variable is large enough before calling the function. This can be achieved by calling VarSetCapacity(MyVar, 123), where 123 is the length that MyVar must be able to hold.

A str argument must not be an expression that evaluates to a number. If it is, the function will not be called and ErrorLevel will be set to -2.

The str type should generally be used with functions that expect LPSTR, LPCSTR, LPTSTR, and similar types.

The asterisk variable "str *" is supported but very rarely used. It can be used with functions that expect something like "char **" or "LPSTR *".

Int64 A 64-bit integer, whose range is -9223372036854775808 (-0x8000000000000000) to 9223372036854775807 (0x7FFFFFFFFFFFFFFF).
Int

A 32-bit integer (the most common integer type), whose range is -2147483648 (-0x80000000) to 2147483647 (0x7FFFFFFF). An Int is sometimes called a "Long".

An Int should also be used for each BOOL argument expected by a function (a BOOL value should be either 1 or 0).

An unsigned Int (UInt) is also used quite frequently, such as for DWORD and COLORREF. It is also used for almost all handles, such as HWND, HBRUSH, and HBITMAP.

Short A 16-bit integer, whose range is -32768 (-0x8000) to 32767 (0x7FFF). An unsigned Short (UShort) can be used with functions that expect a WORD.
Char An 8-bit integer, whose range is -128 (-0x80) to 127 (0x7F). An unsigned character (UChar) can be used with functions that expect a BYTE.
Float A 32-bit floating point number, which provides 6 digits of precision.
Double A 64-bit floating point number, which provides 15 digits of precision.
* or P
(suffix)

Append an asterisk (with optional preceding space) to any of the above types to cause the address of the argument to be passed rather than the value itself (the called function must be designed to accept it). Since the value of such an argument might be modified by the function, whenever a naked variable is passed as the argument, that variable's contents will be updated. For example, the following call would pass the current contents of MyVar to MyFunc but would also update MyVar to reflect any changes made to it by MyFunc: DllCall("MyDll\MyFunc", "int *", MyVar)

In general, an asterisk is used whenever a function has an argument type or return type that starts with "LP" (except a string type such as LPSTR, for which "str" should be used). The most common example is LPDWORD, which is a pointer to a DWORD. Since a DWORD is an unsigned 32-bit integer, use "UInt *" to represent LPDWORD.

Note: "char *" is not the same as "str" because "char *" passes the address of an 8-bit number, but "str" passes the address of a series of characters.

U (prefix)

Prepend the letter U to any of the integer types above to interpret it as an unsigned integer (UInt64, UInt, UShort, and UChar). Strictly speaking, this is necessary only for return values and asterisk variables because it does not matter whether an argument passed by value is unsigned or signed (except for Int64).

A 32-bit unsigned integer (UInt) is an appropriate substitute for any DWORD, HWND, or similar argument expected by a function. Also, an HWND value (the handle of a window) is the same as the window's unique ID.

If a negative integer is specified for an unsigned argument, the integer will wrap around into the unsigned domain. For example, when -1 is sent as a UInt, it would become 0xFFFFFFFF. However, this technique is not supported for 64-bit integers (UInt64).

Unsigned 64-bit integers are not fully supported. Therefore, to work with numbers greater or equal to 0x8000000000000000, omit the U prefix and interpret any negative values received from the function as large integers. For example, a function that yields -1 as an Int64 is really yielding 0xFFFFFFFFFFFFFFFF if it is designed to yield a UInt64.

 

ErrorLevel

ErrorLevel is set to one of the following values to indicate whether the call succeeded or failed.

0: Success.

-1 (negative 1): The [DllFile\]Function parameter is an expression that evaluates to a number. A string is required.

-2: The return type or one of the specified arg types is invalid. This error can also be caused by passing an expression that evaluates to a number to a string (str) argument.

-3: The specified DllFile could not be accessed. If no absolute path was specified for DllFile, the file must exist in the system's PATH or in A_WorkingDir. This error might also occur if the user lacks permission to access the file.

-4: The specified function could not be found inside the DLL.

N (any positive number): The function was called but it aborted with fatal exception number N (for example, 0xC0000005 means "access violation"). In such cases, the function returns a blank value (empty string), but any asterisk variables will still be updated. An example of a fatal exception is dereferencing an invalid pointer such as NULL. Since a Cdecl function never produces the "An" error in the next paragraph, it may generate an exception when too few arguments are passed to it.

An (the letter A followed by an integer n): The function was called but was passed too many or too few arguments. "n" is the number of bytes by which the argument list was incorrect. If n is positive, too many arguments (or arguments that were too large) were passed, or the call requires CDecl. If n is negative, too few arguments were passed. This situation should be corrected to ensure reliable operation of the function. The presence of this error may also indicate that an exception occurred, in which case the function returns a blank value.

 

Performance

When making repeated calls to a DLL, performance can be dramatically improved by loading it explicitly (this is not necessary for a standard DLL such as User32 because it is always resident). This practice avoids the need for DllCall to internally call LoadLibrary and FreeLibrary each time. For example:

hModule := DllCall("LoadLibrary", "str", "MyFunctions.dll")  ; Avoids the need for DllCall() in the loop to load the library.
Loop, C:\My Documents\*.*, , 1
	result := DllCall("MyFunctions\BackupFile", "str", A_LoopFileFullPath)
DllCall("FreeLibrary", "UInt", hModule)  ; It is best to unload the DLL after using it (or before the script exits).

 

Structures and Arrays [v1.0.36.07+]

A structure is a collection of members (fields) stored adjacently in memory. Most members tend to be integers.

Functions that accept the address of a structure (or a memory-block array) can be called by storing the structure's raw binary data in a normal variable. The following steps are generally used:

1) Call VarSetCapacity(MyStruct, 123, 0) to ensure that the target variable is large enough to hold the structure's data. Replace 123 with a number that is at least as large as the size of the structure. Specifying zero as the last parameter is optional; it initializes all members to be binary zero, which is typically used to avoid calling InsertInteger() as often in the next step.

2) If the target function uses the values initially in the structure, call InsertInteger(123, MyStruct, 4) to initialize any members that should be non-zero. Replace 123 with the integer to be put into the target member (or specify &Var to store the address of a variable). Replace 4 with the offset of the target member (see step #4 for description of "offset").

3) Call the target function, passing MyStruct as a "str" argument. For example, DllCall("MyDll\MyFunc", str, MyStruct). The function will examine and/or change some of the members.

4) Use MyInteger := ExtractInteger(MyStruct, 4) to retrieve any desired integers from the structure. Replace 4 with the offset of the target member in the structure. The first member is always at offset 0. The second member is at offset 0 plus the size of the first member (typically 4). Members beyond the second are at the offset of the previous member plus the size of the previous member. Most members -- such as DWORD, Int, and other types of 32-bit integers -- are 4 bytes in size.

The following are the functions referenced above. They can be pasted into any script or included via #Include. See Structure Examples for actual usages.

ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
	Loop %pSize%  ; Build the integer by adding up its bytes.
		result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
	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(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
	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, UChar, pInteger >> 8*(A_Index-1) & 0xFF)
}

 

Remarks

In spite of the built-in exception handling, it is still possible to crash a script with DllCall. This can happen when a function does not directly generate an exception but yields something inappropriate, such as a bad pointer or a string that is not terminated. This might not be the function's fault if the script passed it an unsuitable value such as a bad pointer or a "str" with insufficient capacity. A script can also crash when it specifies an inappropriate argument type or return type, such as claiming that an ordinary integer yielded by a function is an asterisk variable or str.

In v1.0.33.01+, when specifying an argument type or return type that does not contain a space or asterisk, the quotes around it may be omitted. For example, str can be used in place of "str" and CDecl in place of "CDecl". In v1.0.34+, the letter P may be used in place of asterisk to allow the quotes to be omitted there as well. For example: UIntP.

A function that returns the address of one of the strings that was passed into it might return an identical string in a different memory address than expected. For example calling CharLower(CharUpper(MyVar)) in a programming language would convert MyVar's contents to lowercase. But when the same is done with DllCall(), MyVar would be uppercase after the following call because CharLower would have operated on a different/temporary string whose contents were identical to MyVar:

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

To work around this, change the two underlined "str" values above to UInt. This interprets CharUpper's return value as a pure address that will get passed as an integer to CharLower.

 

Related

VarSetCapacity, Functions, 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 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: Change the desktop wallpaper to the specified bitmap (.bmp) file. DllCall("SystemParametersInfo", UInt, 0x14, UInt, 0, Str, WinDir . "\winnt.bmp", UInt, 2)
; 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: A hotkey to temporarily reduce the mouse cursor's speed for precise positioning. ; Hold down the F1 key to slow down the cursor. Release it to return to original speed. F1:: SPI_GETMOUSESPEED = 0x70 SPI_SETMOUSESPEED = 0x71 ; Retrieve the current speed so that it can be restored later: DllCall("SystemParametersInfo", UInt, SPI_GETMOUSESPEED, UInt, 0, UIntP, OrigMouseSpeed, UInt, 0) ; Now set the mouse to the slower speed specified in the next-to-last parameter (the range is 1-20, 10 is default): DllCall("SystemParametersInfo", UInt, SPI_SETMOUSESPEED, UInt, 0, UInt, 3, UInt, 0) KeyWait F1 ; This prevents keyboard auto-repeat from doing the DllCall repeatedly. return F1 up::DllCall("SystemParametersInfo", UInt, 0x71, UInt, 0, UInt, OrigMouseSpeed, UInt, 0) ; Restore the original speed.
; 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.
; Structure Example: Change the system's clock to the specified date and time. Use caution when ; changing to a date in the future as it may cause scheduled tasks to run prematurely! SetSystemTime("20051008142211") ; Pass it a timestamp (local, not UTC). SetSystemTime(YYYYMMDDHHMISS) ; Sets the system clock to the specified date and time. ; Caller must ensure that the incoming parameter is a valid date-time stamp ; (local time, not UTC). Returns non-zero on success and zero otherwise. { ; Convert the parameter from local time to UTC for use with SetSystemTime(). UTC_Delta -= %A_NowUTC%, Seconds ; Seconds is more accurate due to rounding issue. UTC_Delta := Round(-UTC_Delta/60) ; Round to nearest minute to ensure accuracy. YYYYMMDDHHMISS += %UTC_Delta%, Minutes ; Apply offset to convert to UTC. VarSetCapacity(SystemTime, 16, 0) ; Struct consists of 8 UShorts (i.e. 8*2=16) StringLeft, Int, YYYYMMDDHHMISS, 4 ; YYYY (year) InsertInteger(Int, SystemTime, 0, 2) StringMid, Int, YYYYMMDDHHMISS, 5, 2 ; MM (month of year, 1-12) InsertInteger(Int, SystemTime, 2, 2) StringMid, Int, YYYYMMDDHHMISS, 7, 2 ; DD (day of month) InsertInteger(Int, SystemTime, 6, 2) StringMid, Int, YYYYMMDDHHMISS, 9, 2 ; HH (hour in 24-hour time) InsertInteger(Int, SystemTime, 8, 2) StringMid, Int, YYYYMMDDHHMISS, 11, 2 ; MI (minute) InsertInteger(Int, SystemTime, 10, 2) StringMid, Int, YYYYMMDDHHMISS, 13, 2 ; SS (second) InsertInteger(Int, SystemTime, 12, 2) return DllCall("SetSystemTime", str, SystemTime) }
; Structure Example: See the WinLIRC client script for a demonstration of how to use DllCall() to make ; a network connection to a server and receive data from it.