I have a C++ DLL I want to call into from Python. I have no control over the C++ DLL nor do I have its source code or headers.
The mangled functions are of the form:
2980 BA3 005A3060 ?getFoo@FooLib@@YAAEAVFoo@1@XZ
2638 A4D 005A3020 ?getApplicationData@Foo@FooLib@@QEAAAEAVApplicationData@2@XZ
2639 A4E 005A3030 ?getApplicationData@Foo@FooLib@@QEBAAEBVApplicationData@2@XZ
2738 AB1 000F8A30 ?getDataRootPath@ApplicationData@FooLib@@QEBA?AV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@XZ
With the aid of Copilot, I was able to translate these to (crossing fingers that this is right):
Foo __cdecl FooLib::getFoo()
ApplicationData& __thiscall FooLib::Foo::getApplicationData()
const ApplicationData& __thiscall FooLib::Foo::getApplicationData() const
std::wstring __thiscall FooLib::ApplicationData::getDataRootPath() const
From Python, with ctypes docs, I was able assemble this:
from ctypes import *
dll = WinDLL(r"c:\path\to\Foo.dll")
getFoo = getattr(dll, "?getFoo@FooLib@@YAAEAVFoo@1@XZ")
getFoo.argtypes = []
getFoo.restype = c_void_p
getAppData = getattr(dll, "?getApplicationData@Foo@FooLib@@QEAAAEAVApplicationData@2@XZ")
getAppData.argtypes = [c_void_p]
getAppData.restype = c_void_p
getAppData(getFoo()) # returns a pointer reliably, same value each time
Note that as far as Python ctypes is concerned, the two DLL entries for getApplicationData produce the same values.
However, the final function does not work, crashing Python or throwing an Access Violation, likely because it returns a C++ string type, and ctypes cannot handle that. Best recommendations I've seen have been to create a shim DLL that can call the C++ function and convert it to a C string, which Python ctypes can handle.
On the shim side, I can load the function pointer addresses, but two parts seem to be a problem for the getApplicationData and getDataRootPath functions, which are member functions of the Foo class and ApplicationData classes, respectively. Solutions to this kind of problem on the internet seem to be sparse, or at minimum not fully explained. I'm sure there are also subtleties about addresses and pointers in C++ that are getting in my way as well.
So: How can I solve this issue?
__thiscallcalling convention isn't support byctypes("C" types).getApplicationDatacall (seems to) work though, I just pass the pointer fromgetFoo()as the first argument.export "C"wrappers is the way to go.