Your exports use the cdecl
calling convention, not stdcall
, so you need to use CDLL
instead of WinDLL
.
test.cpp:
#include <iostream>
#include <string>
using namespace std;
class A {
string name;
public:
A(const string& name) {
this->name = name;
cout << name << ": signing on" << endl;
}
~A() {
cout << name << ": signing off" << endl;
}
void someFunc() {
cout << name << ": calling someFunc" << endl;
}
};
extern "C" {
__declspec(dllexport) A *A_new(const char *name) {
return new A(string(name));
}
__declspec(dllexport) void A_someFunc(A *obj) {
obj->someFunc();
}
__declspec(dllexport) void A_destruct(A *obj) {
delete obj;
}
}
test.py:
import ctypes
lib = ctypes.CDLL('test.dll')
def opaque_ptr(name):
cls = type(name, (ctypes.Structure,), {})
return ctypes.POINTER(cls)
class A(object):
_A = opaque_ptr('CPP_A')
lib.A_new.restype = _A
lib.A_new.argtypes = ctypes.c_char_p,
lib.A_destruct.argtypes = _A,
lib.A_someFunc.argtypes = _A,
def __init__(self, name, func=lib.A_new):
self._obj = func(name.encode('ascii'))
def __del__(self):
self.destruct()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.destruct()
def destruct(self, func=lib.A_destruct):
if self._obj:
func(self._obj)
self._obj = None
def some_func(self, func=lib.A_someFunc):
if not self._obj:
raise RuntimeError
func(self._obj)
with A('test') as a:
a.some_func()
Output:
test: signing on
test: calling someFunc
test: signing off
FYI, WinDLL
is a subclass of CDLL
. The only change is that it sets _FUNCFLAG_STDCALL
in the flags of the function pointers that it creates instead of _FUNCFLAG_CDECL
.
cdll
and windll
are LibraryLoader
instances. These are more useful in Windows, which automatically supplies the .dll extension. For example, you can use cdll.test.A_new
. When used like this, cdll
caches the loaded CDLL
instance, which in turn caches function pointers.
Due to the above caching, avoid using the global loader instances when creating a library. Your argtypes
, restype
, and errcheck
definitions on function pointers may conflict with other libraries. Instead use CDLL
or a private loader such as cdll = LibraryLoader(CDLL)
.
Also, cdll.LoadLibrary
returns an non-cached instance of CDLL
. There's no reason to ever call it in place of using CDLL
directly.