2009-07-06 5 views
7

myPythonClient (abajo) quiere invocar una función ringBell (cargada desde una DLL usando ctypes). Sin embargo, intentar acceder al ringBell a través de su nombre da como resultado un AttributeError. ¿Por qué?Python: accediendo a la función DLL usando ctypes - acceso por función * nombre * falla

RingBell.h contiene

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

RingBell.cpp contiene

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

myPythonClient.py contiene

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

Respuesta

7

Tal vez porque el nombre de C++ está destrozado por el compilador y no exporta desde el DLL como RingBell. ¿Ha comprobado que aparece en los nombres exportados exactamente así?

+2

Has acertado. Utilicé lo siguiente para descubrir el nombre "mutilado": link.exe/dump/exports RingBell.dll y descubrí que, en el archivo DLL, el nombre de la función era "? RingBell @ MyClass @ MyNamespace @@ SAHXZ ". ¡Gracias! –

+2

También mencionaré que utilicé la función getattr de Python para obtener una referencia a la función ringBell: myRingBellFunction = getattr (cdll.RingBell, "? RingBell @ MyClass @ MyNamespace @@ SAHXZ") myRingBellFunction() # invoca la función otra vez –

10

Su compilador C++ está destruyendo los nombres de todos los objetos visibles externamente para reflejar (así como sus nombres subyacentes) sus espacios de nombres, clases y firmas (así es como la sobrecarga es posible).

Para evitar este cambio, necesita un extern "C" en nombres visibles externamente que desee que sean visibles desde código que no sea C++ (y por lo tanto dichos nombres no pueden sobrecargarse, ni en C++ estándar pueden estar en línea, dentro de espacios de nombres , o dentro de las clases, aunque algunos compiladores de C++ extienden el estándar en algunas de estas direcciones).

+0

¡Mi agradecimiento a usted también! Voy a intentar con "extern". –

+0

¡Intenté probar "extern" y funcionó!Solución está por debajo de los futuros lectores: # include extern "C" __declspec (dllexport) int __cdecl ringBell (void) { \t \t std :: cout << "\ A"; \t return 0; \t} –

7

Todo está trabajando ahora :) Para resumir sus mensajes:

escritura DLL en 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; 
} 

continuación, puede utilizar link.exe programa para ver una función real en la DLL. link.exe es por ejemplo en MSVC2010 aquí:

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

uso:

link /dump /exports yourFileName.dll 

ves algo como:

ordinal hint RVA  name 
     1 0 00001040 [email protected]@[email protected] = [email protected]@[email protected] (int __cdecl MyAdd2(int,int)) 
     2 1 00001030 MyAdd = _MyAdd 

Luego, en Python se puede importar como:

import ctypes 

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

#mc.MyAdd2(1,2) # this Won't Work - name is different in dll 
myAdd2 = getattr(mc,"[email protected]@[email protected]") #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 
Cuestiones relacionadas