【问题标题】:Python: accessing DLL function using ctypes -- access by function *name* failsPython:使用 ctypes 访问 DLL 函数——通过函数 *name* 访问失败
【发布时间】:2009-07-06 17:02:09
【问题描述】:

myPythonClient(下)想要调用ringBell 函数(使用ctypes 从DLL 加载)。但是,尝试通过其name 访问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

【问题讨论】:

    标签: python dll ctypes attributeerror


    【解决方案1】:

    您的 C++ 编译器正在修改所有外部可见对象的名称,以反映(以及它们的底层名称)它们的命名空间、类和签名(这就是重载成为可能的方式)。

    为了避免这种混乱,您需要一个extern "C" 用于您希望在非 C++ 代码中可见的外部可见名称(因此这些名称不能重载,在 C++ 标准中它们也不能内联,在命名空间,或在类中,尽管一些 C++ 编译器在其中一些方向上扩展了标准)。

    【讨论】:

    • 我确实尝试了“extern”,它成功了!未来读者的解决方案如下: #include extern "C" __declspec(dllexport) int __cdecl ringBell ( void ) { std::cout
    【解决方案2】:

    现在一切正常 :) 总结您的帖子:

    用 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;
    } 
    

    然后你可以使用程序li​​nk.exe查看dll中的真实函数名。 link.exe 例如在 MSVC2010 中:

    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
    

    【讨论】:

      【解决方案3】:

      可能是因为 C++ 名称被编译器破坏了,而不是从 DLL 中导出为 RingBell。您是否检查过它在导出的名称中出现的完全一样?

      【讨论】:

      • 你是对的。我使用以下内容发现了“损坏”的名称:link.exe /dump /exports RingBell.dll 并发现在 DLL 中,函数的名称是“?ringBell@MyClass@MyNamespace@@SAHXZ”。谢谢!
      • 我还要提一下,我使用 Python 的 getattr 函数来获取 ringBell 函数的引用: myRingBellFunction = getattr(cdll.RingBell, "?ringBell@MyClass@MyNamespace@@SAHXZ") myRingBellFunction( ) # 再次调用函数
      猜你喜欢
      • 2021-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-27
      • 1970-01-01
      相关资源
      最近更新 更多