美文网首页
应该是史上最全的python调用C接口

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

作者: 还少一双小脚丫 | 来源:发表于2018-11-10 15:16 被阅读0次

    这段时间需要用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,应该基本都讲到了,后续有遇到的坑再继续填

    相关文章

      网友评论

          本文标题:应该是史上最全的python调用C接口

          本文链接:https://www.haomeiwen.com/subject/ssmixqtx.html