티스토리 뷰

목차

    반응형

    파이썬 CTypes 구조체 자세히 알아보기 (PyThon, 파이선)


    파이썬에서 외부 라이브러리를 쓰기 위해서는 확장 모듈을 만들고 그 속에서 외부 라이브러리를 호출하는 방법이 있습니다. 사실 이보다 훨씬 더 간단한 방법이 있었는데, 바로 파이선에서 제공하는 파이썬 ctypes 모듈을 사용하는 것입니다. ctypes 모듈을 이용하면 C의 데이터 타입이나, DLL 혹은 공유 라이브러리(shared library)의 함수를 직접 사용할 수 있습니다.


    파이썬 ctypes 구조체 파이선[Python 자료형] 확장 모듈과 라이브러리


    파이선 ctypes를 사용하는 방법은 매우 간단합니다. 다음 예제를 보겠습니다.


    1
    2
    3
    4
    5
    6
    >>> import ctypes     <- 윈도우에서 테스트 한 결과.
    >>> print(ctypes.windll.kernel32)
    <WinDLL 'kernel32', handle   ...  at   ...>
     
    >>> print(ctypes.cdll.msvcrt)
    <CDLL 'msvcrt', handle  ...  at  ...>
    cs


    파이썬 ctypes를 임포트하는 것으로 모든 준비가 끝납니다.

    위 예제에서는 'stdcall' 방식의 함수는 windll로, 'cdecl' 방식의 함수는 cdll을 통해 호출하는 것을 보여줍니다. 이제는 직접 윈도우에 들어 있는 kernel32.dll 안의 함수를 호출해 보겠습니다.


    1
    2
    >>> print(hex(ctypes.windll.kernel32.GetModuleHandleA(None)))
    0x1d000000
    cs


    참조 사항


    stdcall와 cdecl은 함수 호출 규약이며, 함수가 호출될 때 매개변수가 스택에서 어떻게 전달되는지에 따라 stdcall과 cdecl로 나눌 수 있습니다.


    stdcall은 불려진 함수가 스택에서 인자를 꺼내는(pop) 것이고, cdecl은 호출한 함수에서 스택의 인자를 꺼냅니다.


    ctypes 파이썬 구조체[Python 자료형] 확장 모듈과 라이브러리


    파이썬 ctypes는 외부 함수 호출 말고도 C의 데이터 타입을 쓸 수가 있습니다. 아래는 기본적인 ctypes 함수가 지원하는 타입니다.


    ctypes 타입

    C 타입

    Python 타입

     c_short

     Short

     int

     c_ushort

     unsigned short

     int

     c_int

     Int

     int

     c_uint

     unsigned int

     int

     c_long

     Long

     int

     c_ulong

     usigned long

     int

     c_longlong

     __int64 혹은 long long

     int

     c_ulonglong

     unsigned __int64 혹은

     unsigned long long

     int

     c_float

     Float

     float

     c_double

     Double

     float

     c_longdouble

     long double

     float

     c_char_p

     char * (NULL terminated)

     unicode 혹은 None

     c_wchar_p

     wchar_t * (NULL terminated)

     unicode 혹은 None


    이를 이용해 간단한 예제를 만들겠습니다.


    1
    2
    3
    4
    5
    >>> from ctypes import *
    >>> i = c_int(10)
     
    >>> print("ctypes : ", i ,", value : ", i.value )
    ctypes : c_long(10), value : 10
    cs


    또한 다음 예제와 같이 파이썬 ctypes는 포인터도 지원합니다.


    1
    2
    3
    4
    >>> k = c_int(10)
    >>> pk = pointer(k)     <- c_int(10)의 포인터 값을 가져옵니다.
    >>> print("position :", pointer(k), ", contents :", pk.contents)
    position : <__main__.LP_c_long object at 0x018931C0> , contents : c__long(10)
    cs

    게다가 구조체와 유니온(union)은 다음과 같이 표현할 수 있습니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    >>> import ctypes
    >>> class POINT(ctypes.Structure):
        _fields_ = [("X", ctypes.c_int), ("y", ctypes.c_int)]
    >>> point = POINT(1020)
    >>> print(point.x, point.y)
    10 20
     
    >>> point = POINT(y=5)
    >>> print(point.x, point.y)
    0 5

    cs


    구조체와 유니온은 파이선에서 class로 표현되기 때문에 일단 class 객체를 생성하고, _fields_ 멤버 변수를 이용해 각 멤버 변수를 설정해주는 것입니다.


    파이썬 CTypes 구조체 자세히 알아보기 (PyThon, 파이선)[Python 자료형] 확장 모듈과 라이브러리


    다음 예제에서는 실제로 파이썬 ctypes을 어떻게 사용하는지 보겠습니다. 우선 윈도우에는 사용자에게 메시지 박스를 보여주는 MessageBoxA()라는 함수가 정의돼 있습니다. winuser.h 헤더파일에 정의돼 있는 MessageBoxA는 다음과 같습니다.


    1
    2
    3
    4
    5
    6
    WINUSERAPI int WINAPI
    MessageBoxA(
        HWND hWnd,
        LPCSTR lpText,
        LPCSTR lpCaption,
        UINT uType);
    cs


    ctypes 모듈을 이용해 이를 래핑(wrapping)해 보겠습니다.


    1
    2
    3
    4
    5
    >>> from ctypes import c_int, WINFUNCTYPE, windll
    >>> from ctypes.wintypes import HWND, LPCSTR, UINT
    >>> prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT)
    >>> paramflags = (1"hwnd"0), (1"text""Hi"), (1"caption", None), (1"flags"0)
    >>> MessageBox = prototype(("MessageBoxA", windll.user32), paramflags)
    cs


    이제 Python에서 MessageBox는 윈도우의 MessageBoxA() 함수를 가리킵니다. MessageBox를 아래와 같이 호출해 보겠습니다.


    1
    2
    >>> MessageBox(text="Please, programming with python")
    >>> MessageBox(text="Do you know about python?", flags=3)
    cs


    그리하면 윈도우의 메시지 창이 나타나는 것을 볼 수 있습니다.



    파이썬 CTypes 구조체 자세히 알아보기 (PyThon, 파이선)

    반응형