2009-07-06 19 views
7

myPythonClient (poniżej) chce wywołać funkcję ringBell (ładowaną z biblioteki DLL za pomocą ctypes). Jednak próba uzyskania dostępu do ringBell za pośrednictwem jego nazwy o nazwie skutkuje uzyskaniem AttributeError. Czemu?Python: dostęp do funkcji DLL przy użyciu ctypes - dostęp przez funkcję * nazwa * kończy się niepowodzeniem

RingBell.h zawiera

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

RingBell.cpp zawiera

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

myPythonClient.py zawiera

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

Odpowiedz

7

Może dlatego, że C nazwa ++ jest zniekształcone przez kompilator i nie wywiezione z DLL jako RingBell. Czy sprawdziłeś, czy w takich wyeksportowanych nazwach dokładnie się pojawia?

+2

Masz rację. Użyłem następujących sposobów, aby odkryć "zniekształconą" nazwę: link.exe/dump/exports RingBell.dll i odkryłem, że w bibliotece DLL nazwa funkcji brzmiała: "? RingBell @ MyClass @ MyNamespace @@ SAHXZ ". Dziękujemy! –

+2

Wspomnę również, że użyłem funkcji getattr Pythona, aby uzyskać odwołanie do funkcji RingBell: myRingBellFunction = getattr (cdll.RingBell, "? RingBell @ MyClass @ MyNamespace @@ SAHXZ") myRingBellFunction() # wywołanie funkcji jeszcze raz –

10

Twój kompilator C++ niszczy nazwy wszystkich widocznych z zewnątrz obiektów do odzwierciedlenia (jak również ich nazw bazowych) ich przestrzeni nazw, klas i sygnatur (w ten sposób możliwe staje się przeciążanie).

Aby uniknąć tego zniekształcania, potrzebujesz extern "C" widocznych z zewnątrz nazw, które mają być widoczne z kodu innego niż C++ (i dlatego nie można przeciążać takich nazw, ani w standardzie C++ mogą być wstawiane w obrębie przestrzeni nazw lub w klasach, chociaż niektóre kompilatory C++ rozszerzają standard w niektórych z tych kierunków).

+0

Dziękuję również tobie! Spróbuję "extern". –

+0

Próbowałem dać "extern" i zadziałało!Rozwiązaniem jest poniżej dla przyszłych czytelników: #include extern "C" __declspec (dllexport) int __cdecl ringBell (void) \t { \t std :: cout << "a \"; \t return 0; \t} –

7

Wszystko działa teraz :) Podsumowując swoje posty:

Zapis DLL w 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; 
} 

Następnie można użyć programu link.exe aby zobaczyć prawdziwą nazwę funkcji w bibliotece DLL. link.exe jest na przykład w MSVC2010 tutaj:

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

zastosowanie:

link /dump /exports yourFileName.dll 

widzisz coś takiego:

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 

Następnie w Pythonie można zaimportować go jako:

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 
Powiązane problemy