1 生成被調(diào)用的C dll
1.1 C文件編譯為C動態(tài)鏈接庫
- 在windows下,需要配置visual studio的工程設(shè)置,將工程編譯為dll,細節(jié)不在這里贅述
- gcc環(huán)境下,需要編譯為.so文件,需要修改makefile的鏈接參數(shù),這里也不再贅述
1.2 用于外部引用的C函數(shù)聲明
1.2.1 聲明用于作為dll符號給外部調(diào)用
在函數(shù)聲明加入前綴,如
__declspec(dllexport) int Fun(int a, int b)
否則在加載該dll時會提示找不到該符號
在windows下可以通過vs自帶的dumpbin工具查看可被調(diào)用符號
dumpbin /exports test.dll
1.2.2 C函數(shù)的調(diào)用規(guī)定
C函數(shù)在調(diào)用過程中關(guān)于參數(shù)傳遞和壓棧由多種規(guī)定,作為dll提供給其他程序調(diào)用時,必須明確并統(tǒng)一為同一種調(diào)用規(guī)定,否則會導(dǎo)致棧破壞,編譯器負(fù)責(zé)具體實現(xiàn)調(diào)用規(guī)定,主要有以下幾種調(diào)用規(guī)定
| 調(diào)用規(guī)定 | 聲明 | 編譯符號修飾 | 調(diào)用規(guī)則 | 說明 |
|---|---|---|---|---|
| _stdcall | __declspec(dllexport) int __stdcall fun(int a, int b) |
_fun@number | 參數(shù)從右向左入棧,調(diào)用者壓棧,被調(diào)者負(fù)責(zé)彈棧 | win32 API默認(rèn)調(diào)用規(guī)則 |
| _cdecl | __declspec(dllexport)int __cdecl fun(int a, int b) |
_fun | 參數(shù)從右向左入棧,調(diào)用者負(fù)責(zé)壓棧和彈棧 | C/C++默認(rèn)調(diào)用規(guī)則 |
| _fastcall | __declspec(dllexport)int __fastcall fun(int a, int b) |
@fun@number | 寄存器和棧共同參數(shù)與參數(shù)傳遞 | 寄存器傳參提高性能,難以跨平臺 |
2 ctypes加載dll庫接口
python下調(diào)用C庫有多種方式,ctypes是其中一種比較方便的,調(diào)用時首先需要加載dll文件,根據(jù)C dll的調(diào)用規(guī)定不同需要使用不同接口,使用ctypes需要import ctypes庫
- 對于stdcall的C dll
import ctypes
Objdll = ctypes.windll.LoadLibrary("dllpath") #接口1
Objdll = ctypes.WinDLL("dllpath") #接口2
以上兩種接口都是可用的
- 對于cdecl的C dll
import ctypes
Objdll = ctypes.cdll.LoadLibrary("dllpath")
Objdll = ctypes.CDLL("dllpath")
對于簡單的C函數(shù),例如int add(int a, int b), 此時就可以直接調(diào)用了,如
import ctypes
Objdll = ctypes.cdll.LoadLibrary("dllpath")
Objdll = ctypes.CDLL("dllpath")
c = Objdll.all(1,3)
print(c) # 4
3 ctypes調(diào)用C函數(shù)參數(shù)傳遞
對于較復(fù)雜的C函數(shù)的參數(shù)情況,ctypes調(diào)用時對入?yún)⒑统霾妥鲆欢ㄌ幚?,這里分情況討論
3.1 出參為指針
- C代碼
/* Divide two numbers */
int divide(int a, int b, int *remainder)
{
int quot = a / b;
*remainder = a % b;
return quot;
}
- python代碼
# int divide(int, int, int *)
_divide = _mod.divide_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))_divide.restype = ctypes.c_int
def divide(x, y):
rem = ctypes.c_int()
quot = _divide(x, y, rem)
return quot,rem.value
3.2 入?yún)樽址?/h2>
- C代碼
void PrintfInfo(char *str)
{
printf("the str is %s\n", str);
}
- python代碼
sBuf = 'aaaaaaaaaabbbbbbbbbbbbbb'
pStr = ctypes.c_char_p( )
pStr.value = sBuf.decode() #c_char_p類型的value只接受bytes類型數(shù)據(jù)
dll.PrintInfo(pStr)
這里使用了ctypes的內(nèi)置類型c_char_p,對應(yīng)于C的char數(shù)組,更多ctypes類型與C類型對照在后面附上
3.4 入/出參為結(jié)構(gòu)體
- C代碼
typedef struct
{
char words[10];
}keywords;
typedef struct
{
keywords *kws;
unsigned int len;
}outStruct;
extern "C"int __declspec(dllexport) test(outStruct *o);
int test(outStruct *o)
{
unsigned int i = 4;
o->kws = (keywords *)malloc(sizeof(unsigned char) * 10 * i);
strcpy(o->kws[0].words, "The First Data");
strcpy(o->kws[1].words, "The Second Data");
o->len = i;
return 1;
}
- python代碼
# C結(jié)構(gòu)體需要專門定義class對應(yīng)
class keywords(Structure):
_fields_ = [('words', c_char *10),]
class outStruct(Structure):
_fields_ = [('kws', POINTER(keywords)),
('len', c_int),]
o = outStruct()
dll.test(byref(o))
print o.kws[0].words;
print o.kws[1].words;
print o.len
void PrintfInfo(char *str)
{
printf("the str is %s\n", str);
}
sBuf = 'aaaaaaaaaabbbbbbbbbbbbbb'
pStr = ctypes.c_char_p( )
pStr.value = sBuf.decode() #c_char_p類型的value只接受bytes類型數(shù)據(jù)
dll.PrintInfo(pStr)
這里使用了ctypes的內(nèi)置類型c_char_p,對應(yīng)于C的char數(shù)組,更多ctypes類型與C類型對照在后面附上
typedef struct
{
char words[10];
}keywords;
typedef struct
{
keywords *kws;
unsigned int len;
}outStruct;
extern "C"int __declspec(dllexport) test(outStruct *o);
int test(outStruct *o)
{
unsigned int i = 4;
o->kws = (keywords *)malloc(sizeof(unsigned char) * 10 * i);
strcpy(o->kws[0].words, "The First Data");
strcpy(o->kws[1].words, "The Second Data");
o->len = i;
return 1;
}
# C結(jié)構(gòu)體需要專門定義class對應(yīng)
class keywords(Structure):
_fields_ = [('words', c_char *10),]
class outStruct(Structure):
_fields_ = [('kws', POINTER(keywords)),
('len', c_int),]
o = outStruct()
dll.test(byref(o))
print o.kws[0].words;
print o.kws[1].words;
print o.len
以上包含了幾種主要的參數(shù)傳遞情況,ctypes也提供了一個較為完整的python類型和C類型的對照,如下:

image.png