1.1 ctypes vs Win32
1.2 배열
1.3 익명 구조체/공용체 선언
1.4 pointer type 선언
1.5 ctypes.LP_c_char를 문자열로 변환
2 함수 호출
2.1 Win32 API 호출
2.2 DLL 함수 호출
2.3 함수 인자로 pointer 전달
2.4 함수 인자로 string buffer 전달
2.5 함수 반환값 검사
2.6 명시적인 함수 인자 지정
2.7 명시적인 함수 반환형 지정
2.8 callback 함수
2.8.1 CFUNCTYPE
2.8.2 WINFUNCTYPE
ctypes를 이용해 Win32 환경에서 작업하는 예제 기록.
1 자료구조
1.1 ctypes vs Win32
ctypes.wintypes를 참고한다.
1.2 배열
WCHAR [1000]의 배열을 선언한다고하면
FileNameType = c_wchar * 1000
a = FileNameType()
1.3 익명 구조체/공용체 선언
SYSTEM_INFO 구조체는 아래와 같이 역명 구조체와 공용체를 가지고 있다.
typedef struct _SYSTEM_INFO {
union {
DWORD dwOemId;
struct {
WORD wProcessorArchitecture;
WORD wReserved;
};
};
DWORD dwPageSize;
LPVOID lpMinimumApplicationAddress;
LPVOID lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
WORD wProcessorLevel;
WORD wProcessorRevision;
} SYSTEM_INFO;
이 구조체를 ctypes로 표시하면 아래와 같다.
class _Noname1(ctypes.Structure):
_fields_ = [("wProcessorArchitecture", ctypes.c_ushort),
("wReserved", ctypes.c_short)]
class _Noname2(ctypes.Union):
_anonymous_ = ("s",)
_fields_ = [('dwOemId', ctypes.c_ulong),
('s', _Noname1)]
class SYSTEM_INFO(ctypes.Structure):
_anonymous_ = ("u",)
_fields_ = [("u", _Noname2),
("dwPageSize", ctypes.c_ulong),
("lpMinimumApplicationAddress", ctypes.c_void_p),
("lpMaximumApplicationAddress", ctypes.c_void_p),
("dwActiveProcessorMask", ctypes.c_ulong), # 64 bit에서는 c_longlong이 되어야 한다.
("dwNumberOfProcessors", ctypes.c_ulong),
("dwProcessorType", ctypes.c_ulong),
("dwAllocationGranularity", ctypes.c_ulong),
("wProcessorLevel", ctypes.c_ushort),
("wProcessorRevision", ctypes.c_ushort)]
1.4 pointer type 선언
위에서 선언한 SYSTEM_INFO에 대한 pointer type으로 LPSYSTEM_INFO을 선언한다고 하면
LPSYSTEM_INFO = ctypes.POINTER(SYSTEM_INFO)
1.5 ctypes.LP_c_char를 문자열로 변환
ctypes.LP_c_char를 ctypes.c_char_p로 형변환하면 된다.
ctypes.cast(ctypes.LP_c_char 객체, ctypes.c_char_p).value
2 함수 호출
2.1 Win32 API 호출
ctypes.windll뒤에 원하는 dll 모듈과 함수를 사용하면 된다. 예를 들어 kernel32의 GetsystemInfo함수를 호출한다면 아래와 같이 호출할 수 있다.
ctypes.windll.kernel32.GetsystemInfo( ... )
2.2 DLL 함수 호출
test1.dll의 void __cdecl testfunction1() 함수를 호출한다고 하면
test1 = ctypes.CDLL('test1.dll')
if test1:
test1.testfunction1()
2.3 함수 인자로 pointer 전달
si = SYSTEM_INFO()
ctypes.windll.kernel32.GetSystemInfo(ctypes.byref(si))
2.4 함수 인자로 string buffer 전달
buf = ctypes.create_unicode_buffer(4096)
r = ctypes.windll.kernel32.GetWindowsDirectoryW(buf, 4096)
if r > 0:
print buf.value
2.5 함수 반환값 검사
함수 객체의 errcheck를 지정하면 함수의 반환값 검사를 모아서 할 수 있다. 예를 들어 test2.dll의 BOOL __cdecl testfunction2() 함수에 대해서 코드를 작성해보면 아래와 같다.
>def checkBOOL(result, function, args):
if result == 0:
raise ctypes.WinError()
return args
test2 = ctypes.CDLL('test2.dll')
test2.testfunction2.errcheck = checkBOOL
test2.testfunction2() # 함수 호출이 끝나면 바로 checkBOOL 함수가 호출되서 반환값 검사를 할 수 있다.
2.6 명시적인 함수 인자 지정
함수 객체의 argtypes를 이용해서 함수의 인자를 명시적으로 지정할 수 있다. 예를 들어 test3.dll의 BOOL __cdecl testfunction3(LPCWSTR, LPBOOL)에 대해서 코드를 작성해보면 아래와 같다.
test3 = ctypes.CDLL('test3.dll')
test3.testfunction3.argtypes = [ctypes.c_wchar_p, ctypes.POINTER(ctypes.c_long)]
b = ctypes.c_long()
r = test3.testfunction3(u"hello, world!", ctype.byref(b))
2.7 명시적인 함수 반환형 지정
함수 객체의 restype을 시용해서 함수의 반환형을 명시적으로 지정할 수 있다(함수 반환형이 void라면 None을 사용한다). 예를 들어 test4.dll의 HANDLE __cdecl testfunction4()에 대해서 코드를 작성해보면 아래와 같다.
test4 = ctypes.CDLL('test4.dll')
test4.testfunction4.restype = ctypes.c_void_p
h = test4.testfunction4()
2.8 callback 함수
callback 함수 형식에 따라 ctypes.CFUNCTYPE 또는 ctypes.WINFUNCTYPE을 사용해서 callback 함수 형을 만들면 된다.
2.8.1 CFUNCTYPE
python 문서에 나온 예제를 Win32에 맞게 변형했다.
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
def py_cmp_func(a, b):
print 'py_cmp_func', a[0], b[0]
return 0
cmp_func = CMPFUNC(py_cmp_func)
IntArray5 = ctypes.c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
qsort = ctypes.windll.msvcrt.qsort
qsort.restype = None
qsort(ia, len(ia), ctypes.sizeof(ctypes.c_int), cmp_func)
2.8.2 WINFUNCTYPE
LF_FACESIZE = 32
LF_FULLFACESIZE = 64
class LOGFONT(ctypes.Structure):
_fields_ = [
('lfHeight', ctypes.c_long),
('lfWidth', ctypes.c_long),
('lfEscapement', ctypes.c_long),
('lfOrientation', ctypes.c_long),
('lfWeight', ctypes.c_long),
('lfItalic', ctypes.c_byte),
('lfUnderline', ctypes.c_byte),
('lfStrikeOut', ctypes.c_byte),
('lfCharSet', ctypes.c_byte),
('lfOutPrecision', ctypes.c_byte),
('lfClipPrecision', ctypes.c_byte),
('lfQuality', ctypes.c_byte),
('lfPitchAndFamily', ctypes.c_byte),
('lfFaceName', ctypes.c_wchar * LF_FACESIZE)]
PLOGFONT = ctypes.POINTER(LOGFONT)
class ENUMLOGFONT(ctypes.Structure):
_fields_ = [
('elfLogFont', LOGFONT),
('elfFullName', ctypes.c_wchar * LF_FULLFACESIZE),
('elfStyle', ctypes.c_wchar * LF_FACESIZE)]
PENUMLOGFONT = ctypes.POINTER(ENUMLOGFONT)
if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
LPARAM = ctypes.c_long
elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
LPARAM = ctypes.c_longlong
#int CALLBACK EnumFontFamProc(ENUMLOGFONT *lpelf,__in NEWTEXTMETRIC *lpntm, DWORD FontType, LPARAM lParam;
EnumFontFamProc = ctypes.WINFUNCTYPE(ctypes.c_int, PENUMLOGFONT, ctypes.c_void_p, ctypes.c_long, LPARAM)
def py_enum_font_fam_proc(lpelf, lpntm, FontType, lparam):
print 'py_enum_font_fam_proc', lpelf.contents.elfFullName
return 1
enum_font_proc = EnumFontFamProc(py_enum_font_fam_proc)
EnumFontFamilies = ctypes.windll.gdi32.EnumFontFamiliesW
hdc = ctypes.windll.user32.GetDC(0)
EnumFontFamilies(hdc, 0, enum_font_proc, 0)
ctypes.windll.user32.ReleaseDC(hdc)