Python:使用 ctypes 访问 DLL 函数 - 通过函数 *name* 访问失败

发布于 2024-07-26 09:54:25 字数 910 浏览 8 评论 0原文

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

myPythonClient (below) wants to invoke a ringBell function (loaded from a DLL using ctypes). However, attempting to access ringBell via its name results in an AttributeError. Why?

RingBell.h contains

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

RingBell.cpp contains

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

myPythonClient.py contains

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

似狗非友 2024-08-02 09:54:25

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

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

Your C++ compiler is mangling the names of all externally visible objects to reflect (as well as their underlying names) their namespaces, classes, and signatures (that's how overloading becomes possible).

In order to avoid this mangling, you need an extern "C" on externally visible names that you want to be visible from non-C++ code (and therefore such names cannot be overloaded, nor in C++ standard can they be inline, within namespaces, or within classes, though some C++ compilers extend the standard in some of these directions).

少女七分熟 2024-08-02 09:54:25

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

用 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

All is working now :) To summarize your posts:

Write DLL in C++:

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

Then you can use program link.exe to see real function name in dll.
link.exe is for example in MSVC2010 here:

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

use:

link /dump /exports yourFileName.dll

you see Something like:

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

Then in python you can import it as:

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
没企图 2024-08-02 09:54:25

也许是因为 C++ 名称被编译器破坏了,并且没有作为 RingBell 从 DLL 导出。 您是否检查过它在导出的名称中是否完全一样?

Perhaps because the C++ name is mangled by the compiler and not exported from the DLL as RingBell. Have you checked that it appears in the exported names exactly like that?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文