Python: 使用ctypes访问DLL函数 -- 使用函数名称访问失败

11

myPythonClient(下文)想要调用ringBell函数(使用ctypes从DLL中加载)。然而,尝试通过其名称访问ringBell会导致AttributeError错误。为什么?

RingBell.h包含:

namespace MyNamespace
    {
    class MyClass
        {
        public:
            static __declspec(dllexport) int ringBell ( void ) ;
        } ;
    }

RingBell.cpp包含内容。

#include <iostream>
#include "RingBell.h"
namespace MyNamespace
    {
    int __cdecl MyClass::ringBell ( void )
        {
        std::cout << "\a" ;
        return 0 ;
        }
    }

myPythonClient.py 包含的内容

from ctypes import *
cdll.RingBell[1]() # this invocation works fine
cdll.RingBell.ringBell() # however, this invocation errors out
# AttributeError: function 'ringBell' not found
3个回答

12
现在一切都正常了 :) 总结一下你的帖子:
用C++编写DLL:
// Header
extern "C"
{   // Name in DLL will be "MyAdd" - but you won't be able to find parameters etc...
    __declspec(dllexport) int MyAdd(int a, int b);
}  
// Name will be with lot of prefixes but some other info is provided - IMHO better approach
__declspec(dllexport) int MyAdd2(int a, int b);

//.cpp Code
__declspec(dllexport) int MyAdd(int a, int b)
{   return a+b;
}
__declspec(dllexport) int MyAdd2(int a, int b)
{   return a+b;
} 

然后,您可以使用程序link.exe来查看dll中的实际函数名称。例如,在MSVC2010中,link.exe在此处:

c:\program files\microsoft visual studio 10.0\VC\bin\link.exe

使用:

link /dump /exports yourFileName.dll

您会看到类似以下的内容:

ordinal hint RVA      name
      1    0 00001040 ?MyAdd2@@YAHHH@Z = ?MyAdd2@@YAHHH@Z (int __cdecl MyAdd2(int,int))
      2    1 00001030 MyAdd = _MyAdd

然后在Python中,您可以将其导入为:
import ctypes

mc = ctypes.CDLL('C:\\testDll3.dll')

#mc.MyAdd2(1,2) # this Won't Work - name is different in dll
myAdd2 = getattr(mc,"?MyAdd2@@YAHHH@Z") #to find name use: link.exe /dump /exports fileName.dll 
print myAdd2(1,2)
#p1 = ctypes.c_int (1) #use rather c types
print mc[1](2,3) # use indexing - can be provided using link.exe

print mc.MyAdd(4,5)
print mc[2](6,7) # use indexing - can be provided using link.exe

12

你的C++编译器将所有外部可见对象的名称进行混淆,以反映它们的命名空间、类和签名(这就是重载变得可能的方式)。

为了避免这种混淆,你需要在希望从非C++代码中访问的外部可见名称上使用extern "C"(因此这些名称不能重载,在C++标准中它们不能在命名空间、类内部或内联,但一些C++编译器在某些方面扩展了标准)。


我确实尝试了"extern",并且成功了!以下是解决方案,供以后的读者参考:#include <iostream> extern "C" __declspec(dllexport) int __cdecl ringBell ( void ) { std::cout << "\a" ; return 0 ; } - JaysonFix

7
也许是因为编译器对C++名称进行了混淆,并且未将其作为“RingBell”导出到DLL中。您是否检查过它在导出的名称中是否完全如此?

2
你是正确的。 我使用以下命令来查找“mangled”名称: link.exe /dump /exports RingBell.dll 并发现在DLL中,该函数的名称为“?ringBell@MyClass@MyNamespace@@SAHXZ”。谢谢! - JaysonFix
2
我还要提到,我使用了Python的getattr函数来获取ringBell函数的引用: myRingBellFunction = getattr(cdll.RingBell, "?ringBell@MyClass@MyNamespace@@SAHXZ") myRingBellFunction() # 再次调用该函数 - JaysonFix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接