컴퓨터잡담

DllCall()

by 디케 posted Dec 22, 2010
?

단축키

Prev이전 문서

Next다음 문서

ESC닫기

크게 작게 위로 아래로 댓글로 가기 인쇄

DllCall()


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


인수명설명
[DllFile]\FunctionDLL의 파일명과 함수명.(을)를 「\」(으)로 단락지어 기술.
디렉토리 패스를 생략 했을 경우, 시스템 폴더 등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 *」라고 하면, 「문자열의 격납된 메모리 영역의 주소」가 격납된 메모리 영역의 주소가 보내지게 된다.
Int6464비트 정수(부호의 유무에 대해서는 「U」프리픽스의 란을 참조)
Int32비트 정수(부호의 유무에 대해서는 「U」프리픽스의 란을 참조)
Short16비트 정수(부호의 유무에 대해서는 「U」프리픽스의 란을 참조)
Char8비트 정수(부호의 유무에 대해서는 「U」프리픽스의 란을 참조)
Float32비트 부동 소수점치
Double64비트 부동 소수점치
「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

VarSetCapacityRegisterCallback()함수PostMessageSysGet

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.