Питон:доступ к функции DLL с использованием ctypes — доступ по функции *имя* невозможен
-
23-08-2019 - |
Вопрос
myPythonClient
(ниже) хочет вызвать ringBell
функция (загружается из DLL с помощью ctypes
).Однако попытка получить доступ 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
Решение
Возможно, потому, что имя C++ искажено компилятором и не экспортируется из DLL, как RingBell
.Вы проверяли, что в экспортируемых именах оно отображается именно так?
Другие советы
Ваш компилятор C++ искажает имена всех видимых извне объектов, чтобы отразить (а также их основные имена) их пространства имен, классы и сигнатуры (именно так становится возможной перегрузка).
Чтобы избежать этого искажения, вам нужен extern "C"
на видимых извне именах, которые вы хотите видеть из кода, отличного от C++ (и, следовательно, такие имена не могут быть перегружены, и в стандарте C++ они не могут быть встроенными, внутри пространств имен или внутри классов, хотя некоторые компиляторы C++ расширяют стандарт в некоторых эти направления).
Теперь все работает :) Подводя итог вашим сообщениям:
Напишите DLL на 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;
}
Затем вы можете использовать программу link.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