Python的學習(三十二)---- ctypes庫的使用整理
Python中ctypes的使用整理
ctypes是Python的一個外部庫,提供和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
假設你已經有了一個的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可呼叫的函式指標,這些通常被稱為回撥函式。
(這個等用到時,再仔細研究。。。)