应该是史上最全的python调用C接口

这段时间需要用python调用C的接口,网上搜了很多,结合python的官方文档,整理下备用
1、加载dll

from ctypes import *
dll = cdll.LoadLibrary('DLL1.dll')#func1
dll = CDLL('DLL1.dll')#func2
print(dll)

2、数据类型的对应


3、函数调用
C

DLL1_API int fnDLL1(void)
{
    return 42;
}

Python

print(dll.fnDLL1())

4、参数传递
C

DLL1_API int fnDLL2(int a, float b, double c, const char * buffer,int &d)
{
    printf("recv : %d,%f,%f,%s,\n", a, b, c, buffer);
    d = 10;
    return 1;
}

int double float 这些类型可以直接传递
char * 直接传递bytes
指针或者引用类型需要用byref或者pointer,也可以用相应类型的指针类型
例如上个接口中传递 int &d 在传递的过程中可以用 byref(temp)

Python

    temp = c_int(0)
    print(dll.fnDLL2(1,c_float(2.0),c_double(3.0),'hell0'.encode('gbk'),byref(temp)))
    print('byref',temp1.value)

也可以用int的指针类型,这个类型需要自己定义,POINTER一般针对类型
而pointer针对实例化以后的对象,比如上面也可以用pointer(temp)

   type_p_int = POINTER(c_int)
    temp = type_p_int(c_int(0))
    print(dll.fnDLL2(1,c_float(2.0),c_double(3.0),'hell0'.encode('gbk'),temp))
    print('int *',temp,temp[0],temp.contents)

返回值
int,float,double 这些类型直接接收就可以
其他类型需要先设置接口的返回类型
C

DLL1_API char * fnDLL3(char *buf)
{
    return buf;
}

python

    dll.fnDLL3.restype = c_char_p
    res = dll.fnDLL3('hello'.encode('gbk'))
    print(res,type(res))

如果传递的是char * 需要改变其内容,需要预先定义好存储空间
C

DLL1_API int fnDLL4(char *buf, size_t buffsize)
{
    printf("%s\n", buf);
    memset(buf, 0, buffsize);
    sprintf(buf, "world");
    return 1;
}

python

    buf = create_string_buffer('hello'.encode('gbk'),10)
    dll.fnDLL4(byref(buf),10)
    print(buf.value)

unicode类型
C

DLL1_API WCHAR * fnDLL10(WCHAR * buf,size_t bufsize)
{
    wprintf(L"wchar:%s\n", buf);
    wmemset(buf, 0, bufsize);
    wsprintf(buf, L"hello world\n");
    return buf;
}

python

    wbuf = create_unicode_buffer("hello",32)
    dll.fnDLL10.restype = c_wchar_p
    res = dll.fnDLL10(byref(wbuf),32)
    print("wchar--",res)

5、结构体定义
我们用 fields = [(‘name1’,type1),(‘name2’,type2)]来表示结构体的成员
字节对齐 C结构体中经常会出现按照指定的字节进行对齐结构体,用pack来指定对齐的字节数,数组的定义直接用 *num 表示个数
C

#pragma pack(1)
struct MyStruct
{
    int a;
    double b;
    char c[32];
};
#pragma pack()

python

class MyStruct(Structure):
    _fields_ = [
        ('a',c_int),
        ('b',c_double),
        ('c',c_char*32),
    ]
    _pack_ = 1

位域
C

struct MyStruct1
{
    int a : 16;
    int b : 16;
};

python

class MyStruct1(Structure):
    _fields_ = [
        ('a',c_int,16),
        ('b', c_int, 16),
    ]

结构体的嵌套
c

struct MyStruct2
{
    int a;
    MyStruct S[4];
};

python

class MyStruct2(Structure):
    _fields_ = [
        ('a',c_int),
        ('struct',MyStruct*4)
    ]

传递结构体,与之前传递参数一样,指针类型用byref或者pointer
c

DLL1_API int fnDLL5(MyStruct & s)
{
    printf("mystruct:\na:%d\nb:%f\nc:%s\n", s.a, s.b, s.c);
    return 1;
}

python

    mystruct = MyStruct()
    mystruct.a = 1
    mystruct.b = 1.0
    mystruct.c = 'helloworld'.encode('gbk')
    dll.fnDLL5(byref(mystruct))
    dll.fnDLL5(pointer(mystruct))

返回结构体,与之前相同,需要指定返回的类型
c

DLL1_API MyStruct fnDLL6()
{
    MyStruct *tem = new MyStruct;
    tem->a = 10;
    tem->b = 20;
    sprintf(tem->c, "hello");
    return *tem;
}

python

    dll.fnDLL6.restype = MyStruct
    res = dll.fnDLL6()
    print(res)
    print('mystruct:', res.a, res.b, res.c)
    del res

高阶数组的定义
int my_array[10][10];

    # 先定义一个数组类型
    type_int_array_10 = c_int * 10
    # 定义数组的数组(即二维数组)
    type_int_array_10_10 = type_int_array_10 * 10
    # 创建二维数组对象
    my_array = type_int_array_10_10()
    # 使用二维数组
    my_array[1][2] = 3

字节流与结构体的相互转换

#pack
    print(string_at(addressof(mystruct),sizeof(mystruct)))
#unpack
    buf = bytes(sizeof(MyStruct))
    assert len(buf)
    buf = create_string_buffer(sizeof(MyStruct))
    res = cast(pointer(buf),POINTER(MyStruct)).contents
    print(res,type(res))
    print('mystruct:',res.a,res.b,res.c)
def Pack(ctype_instance):   
    return string_at(addressof(ctype_instance),sizeof(ctype_instance))
def UnPack(ctype,buf):
    assert sizeof(ctype) == len(buf)
    cstring = create_string_buffer(buf)
    return cast(pointer(cstring),POINTER(ctype)).contents

回调函数
先用CFUNCTYPE 定义回调函数类型,参数的第一个参数为返回值类型
后面的参数为回调函数传递的参数类型,然后定义python中的函数,
C

typedef int (*callbakc) (int a, int b);
DLL1_API void fnDLL7(int a, int b, callbakc func)
{
    int n = func(a, b);
    printf("c++ callback %d\n", n);
}

python

    CMPFUNC = CFUNCTYPE(c_int,c_int,c_int)
    cmp_func = CMPFUNC(callFunc)
    dll.fnDLL7(1,2,cmp_func)

这里有个地方特别注意,如果回调函数中有void* ,char等类型,在python中定义回调函数的时候如果定义为 c_void_p ,c_char_p,实际返回的数据为int,bytes
这时候其实python内部已经把参数的值拿出来了,而我们需要的是char
地址的内容,常用的比如传递某一串字节流,我们需要传递出字节流的长度和首地址的指针,如果直接使用参数,c_void_p拿到的是一个int类型,而c_char_p拿到的是截止到最后一个'\0'的字节,最终我们在python中用string_at 来拿到实际的字节流
c回调

typedef void (*callbakc) (void * buf, int &buf_size);

python中的定义

def callback(buf,size):
    string = string_at(buf,size.value)

CALLBACKFUNC = CFUNCTYPE(None,c_void_p,c_int)
call = CALLBACKFUNC(callback)

OK,应该基本都讲到了,后续有遇到的坑再继续填

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容