从python调用c++函数

Posted

技术标签:

【中文标题】从python调用c++函数【英文标题】:call c++ function from python 【发布时间】:2018-05-06 06:40:43 【问题描述】:

我遇到一个问题,这个dll java或者php可以调用成功,但是当我使用python调用时出现访问冲突错误,

我想知道c++ char*是ctypes c_char_p的错误,我该如何使用python ctypes map c++​​ char*

c++ dll定义

#define Health_API extern "C" __declspec(dllexport)
Health_API unsigned long Initialization(int m_type,char * m_ip,int m_port)
Health_API int GetRecordCount(unsigned long m_handle)

python 代码

from ctypes import *

lib = cdll.LoadLibrary(r"./Health.dll")

inita = lib.Initialization
inita.argtype = (c_int, c_char_p, c_int)
inita.restype = c_ulong

getcount = lib.GetRecordCount
getcount.argtype = c_ulong
getcount.retype = c_int
# here call
handle = inita(5, '127.0.0.1'.encode(), 4000)
print(handle)
result = getcount(handle)

错误信息:

2675930080 
Traceback (most recent call last):
  File "C:/Users/JayTam/PycharmProjects/DecoratorDemo/demo.py", line 14, in <module>
    print(getcount(handle))
OSError: exception: access violation reading 0x000000009F7F73E0

修改后

我发现如果不定义 restype=long ,它会返回负数,这是一个值得注意的问题,但是我更改了 restype=u_int64 ,它也无法连接。

很高兴我的同学用她的电脑(win7 x64)调用同样的dll成功,使用如下简单代码

from ctypes import *

lib = cdll.LoadLibrary("./Health.dll")
handle = lib.Initialization(5, '127.0.0.1'.encode(), 4000)
lib.GetRecordList(handle, '')

我是win10 x64,我的python环境是annaconda,换了和她一样的环境,用一样的代码,还是一样的报错

python 与 java

虽然我知道应该是环境原因,但我很想知道到底是什么原因造成的?

蟒蛇

每次运行手柄都是不规则号,我同学的电脑是固定号

如:288584672,199521248,1777824736,-607161376(我没有设置restype)

this is using python call dll,cann't connect port 4000

java

也可以得到没有负数的常规数

如:9462886128,9454193200,9458325520,9451683632

this's java call dll

所以我认为应该是这个代码导致错误,但在其他电脑上没有问题,这很神奇

handle = lib.Initialization(5, '127.0.0.1'.encode(), 4000)

c++ dll:

Health_API unsigned long Initialization(int m_type,char* m_ip,int m_port)

    CHandleNode* node;
    switch(m_type)
    
    case BASEINFO:
        
            CBaseInfo* baseInfo = new CBaseInfo;
            baseInfo->InitModule(m_ip,m_port);
            node = m_info.AddMCUNode((unsigned long)baseInfo);
            node->SetType(m_type);
            return (unsigned long)baseInfo;
        
        break;
    case BASEINFO_ALLERGENS:
        
            CBaseInfo_Allergens* baseInfo_Allergens = new CBaseInfo_Allergens;
            baseInfo_Allergens->InitModule(m_ip,m_port);
            node = m_info.AddMCUNode((unsigned long)baseInfo_Allergens);
            node->SetType(m_type);
            return (unsigned long)baseInfo_Allergens;
        
        break;
    . (case too many, ... represent them)
    .
    .
    
    return -1;



Health_API int GetRecordList(unsigned long m_handle,char* m_index)

    CHandleNode* node = m_info.GetMCUNode(m_handle);
    if( node == NULL)
    
        return -1;
    
    switch(node->GetType())
    
    case BASEINFO:
        
            CBaseInfo* baseInfo = (CBaseInfo*)m_handle;
            return baseInfo->GetRecordList(m_index);
        
        break;
    case BASEINFO_ALLERGENS:
        
            CBaseInfo_Allergens* baseInfo_Allergens = (CBaseInfo_Allergens *)m_handle;
            return baseInfo_Allergens->GetRecordList(m_index);
        
        break;
    . (case too many, ... represent them)
    .
    .
    
    return -1;

【问题讨论】:

Java 正在返回 64 位值(注意返回数字有多大)。在 python 中,如果您不指定 restype,则默认为 int,这是一个有符号整数,这就是您看到负数的原因。在python中你必须使用c_void_p,正如我已经解释过的,你还应该修复DLL的代码 据我所知,在 Python ctypes u_int64 类型中不存在。正如我已经解释过的,即使 C 函数原型是整数,你也应该使用 c_void_p 你同学的代码是错误的,她只是幸运的是指针适合32位整数 想你,我会尽快修改的 【参考方案1】:

如果有 C++ 的源代码可能会有所帮助,但我可以猜测一下

我不认为这是相关的,但是:

getcount.retype 缺少 s,它应该是 restype。 而且我总是将argtype 定义为一个数组而不是一个元组,如果只有一个参数,我仍然使用一个元素的数组。

如果char* 是一个以空结尾的字符串,您确实需要使用c_char_p。在这种情况下,强烈建议将const 添加到m_ip 参数中。

如果char* 是指向原始缓冲区的指针(由调用函数填充或未填充),此处不是这种情况,则需要使用POINTER(c_char) 而不是c_char_p

根据错误消息,您的 handle 是一个指针,您应该在 C++ 代码中使用正确的类型:void*。在你的python中你应该使用c_void_p

它是 64 位代码吗?使用什么工具链/编译器来构建 DLL?这个工具链中 unsigned long 的大小是多少?

编辑:这是您问题的原因:Is Python's ctypes.c_long 64 bit on 64 bit systems?

handle 是您的 DLL 中的一个指针,并且肯定是使用编译器构建的,其中 long 是 64 位,但对于 Windows 上的 64 位 python,long 是 32 位,它无法存储您的指针。你真的应该使用void*

首先你可以尝试在你的python中使用c_void_p作为句柄而不修改你的源代码,它可能会根据使用的编译器工作。但是由于long类型的大小没有被任何标准定义,并且可能无法存储指针,所以你真的应该在你的C++代码中使用void*uintptr_t

Edit2:如果您使用的是 Visual Studio,而不是 GCC,那么 long 的大小只有 32 位 (Size of long vs int on x64 platform) 所以你必须修复DLL的代码

【讨论】:

谢谢,你觉得win7和win10有区别吗?我想不出更多的区别 不,问题不存在。首先,您的 C++ 代码是错误的,必须按照已经解释的方式进行修复:指针永远不应放入无符号长整数中,具体取决于编译器和平台,它可能不适合整数。请说明 DLL 是如何构建的,使用哪个工具链/编译器以及哪个版本。 DLL使用vs 2010 build ,x64 , release,明天我会修复DLL的代码,现在是1:40,我要睡觉了。 你应该再看一遍我的消息,我不知道你用的是visual studio。问题也出在您的 DLL 源代码中,因为使用 Visual Studio,long 无法在 64 位构建上存储指针 谢谢,我会告诉c++ code wirter,我不懂c++

以上是关于从python调用c++函数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 QT/python 从 Javascript 调用 C++ 函数?

从python调用c++函数

从python调用c++函数

使用 SWIG 包装对象从 C++ 调用 Python 函数的最简洁方法是啥

使用 C++ 指针调用 python 函数

从 C++ 调用 Python