1. 程式人生 > >Python的學習(三十二)---- ctypes庫的使用整理

Python的學習(三十二)---- ctypes庫的使用整理

Python中ctypes的使用整理

ctypesPython的一個外部庫,提供和C語言相容的資料型別,可以很方便地呼叫C DLL中的函式。ctypes的官方文件在這裡

1. ctypes基本資料型別對映表

引數型別預先設定好,或者在呼叫函式時再把引數轉成相應的c_***型別。ctypes的型別對應如下:

ctypes type C type Python Type
c_char char 1-character string
c_wchar wchar_t 1-character unicode string
c_byte char int/long
c_ubyte
unsigned char int/long
c_bool bool bool
c_short short int/long
c_ushort unsigned short int/long
c_int int int/long
c_uint unsigned int int/long
c_long long int/long
c_ulong unsigned long int/long
c_longlong __int64 or longlong int/long
c_ulonglong unsigned __int64 or unsigned long long
int/long
c_float float float
c_double double float
c_longdouble long double float float
c_char_p char * string or None
c_wchar_p wchar_t * unicode or None
c_void_p void * int/long or None

對應的指標型別是在後面加上"_p",如int*是c_int_p等等。在python中要實現c語言中的結構,需要用到類。

2. 載入DLL

訪問dll,首先需引入ctypes庫
from ctypes 

import *   

假設你已經有了一個的DLL(名字是add.dll),且該DLL有一個符合cdecl(這裡強調呼叫約定是因為,stdcall呼叫約定和cdecl呼叫約定宣告的匯出函式,在使用python載入時使用的載入函式是不同的,後面會有說明)呼叫約定的匯出函式Add。
stdcall呼叫約定:兩種載入方式

Objdll = ctypes.windll.LoadLibrary("dllpath")  
Objdll = ctypes.WinDLL("dllpath")   

cdecl呼叫約定:也有兩種載入方式

Objdll = ctypes.cdll.LoadLibrary("dllpath")  
Objdll = ctypes.CDLL("dllpath")  

其實windll和cdll分別是WinDLL類和CDll類的物件。 

3. 呼叫DLL方法

載入dll後會返回一個DLL物件,使用其中的函式方法則相當於操作該物件的對應屬性。

注意,經過stdcall宣告的方法,如果不是用def檔案宣告的匯出函式或者extern “C” 宣告的話,編譯器會對函式名進行修改

函式引數申明,通過設定函式的argtypes屬性

函式返回型別,函式預設返回c_int型別,如果需要返回其他型別,需要設定函式的restype屬性

4. 指標與引用

常用的通過呼叫ctypes型別的指標函式來建立指標例項:

from ctype import *
i = c_int(1)
pi = POINTER(i)

對指標例項賦值只會改變其指向的記憶體地址,而不是改變記憶體的內容,與其他語言類似,如需要可改變內容的字串,可須使用create_string_buffer()

>>> p = create_string_buffer("Hello", 10)  # create a 10 byte buffer
>>> print sizeof(p), repr(p.raw)
10 'Hello/x00/x00/x00/x00/x00'

不帶引數的呼叫指標型別建立一個NULL指標, NULL指標有一個False布林值

>>> null_ptr = POINTER(c_int)() 
>>> print bool(null_ptr) 
False 

指標例項有一個contents屬性,返回這個指標所指向的物件。

另外,byref()是用來傳遞引用引數,pointer()作為傳參通常會建立一個實際的指標物件,當不需要實際指標物件時,則可使用byref()

5. 結構體型別處理

Structures和Unions必須繼承Structure和Union基礎類,它們都在ctypes模組中定義,每一個子類必須定義個_fields_屬性,_fields_是一個二維的tuples列表,包含著每個field的name及type,這field型別必須是一個ctypes型別,如c_int,或者任何其他的繼承ctypes的型別,如Structure, Union, Array, 指標等。

例如有一個簡單結構,包含兩個整型x和y,可如下初始化一個結構:

from ctypes import *
import types
class Test(Structure):
    _fields_ = [('x', c_int),
                ('y', c_char)]
test1 = Test(1, 2)

另外,如結構體用於連結串列操作,即包含指向結構體指標時,若直接定義:

from ctypes import *
import types
class Test(Structure):
    _fields_ = [('x', c_int),
                ('y', c_char),
                ('next', Test)]

則python會報錯type未定義,:

from ctypes import *
import types
class Test(Structure):
    pass
Test._fields_ = [('x', c_int),
                ('y', c_char),
                ('next', POINTER(Test))]

6. 陣列定義

陣列包含一個固定數目的相同型別的例項,常用建立陣列型別是對一個數據型別與一個正數相乘,例如:

ArrayType = Test * 10
初始化和使用陣列:

>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print ii
<c_long_Array_10 object at 0x...>
>>> for i in ii: print i,

7. 回撥函式

ctypes可以從python可呼叫物件中建立一個c可呼叫的函式指標,這些通常被稱為回撥函式。

(這個等用到時,再仔細研究。。。)