通过ctypes在扩展DLL中使用类需要帮助
我在Visual Studio里写了以下代码,目的是创建一个扩展的DLL文件。
class A
{
public:
void someFunc()
{
}
};
extern "C" __declspec(dllexport) A* A_new()
{
return new A();
}
extern "C" __declspec(dllexport) void A_someFunc(A* obj)
{
obj->someFunc();
}
extern "C" __declspec(dllexport) void A_destruct(A* obj)
{
delete obj;
}
我想用ctypes在Python中使用类A。我在wrapper.py里写了以下代码——
从ctypes导入windll
libA = windll.LoadLibrary("c:\ctypestest\test.dll")
类A:
def init(self):
self.obj = libA.A_new()
def __enter__(self):
return self
def __exit__(self):
libA.A_destruct(self.obj)
def some_func(self):
libA.A_someFunc(self.obj)
在Python 2.7.1的命令提示符下,我做了以下操作——
import wrapper as w ----> 运行正常
a = w.A() ----> works fine
a.some_func() ----> Error
libA.A_someFunc(self.obj)
出现了错误:ValueError: 可能是因为调用的参数太多了。(多了4个字节)
请帮帮我。
提前谢谢你,
2 个回答
这应该和在Python中创建任何对象是一样的:
some_var = A()
你的导出使用的是 cdecl
调用约定,而不是 stdcall
,所以你需要用 CDLL
而不是 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()
输出:
test: signing on
test: calling someFunc
test: signing off
顺便说一下,WinDLL
是 CDLL
的一个子类。它唯一的不同之处在于,它在创建的函数指针的标志中设置了 _FUNCFLAG_STDCALL
,而不是 _FUNCFLAG_CDECL
。
cdll
和 windll
是 LibraryLoader
的实例。这在 Windows 系统中更有用,因为 Windows 会自动添加 .dll 后缀。例如,你可以使用 cdll.test.A_new
。这样使用时,cdll
会缓存加载的 CDLL
实例,而这个实例又会缓存函数指针。
由于上面的缓存,创建库时要避免使用全局加载器实例。你的 argtypes
、restype
和 errcheck
在函数指针上的定义可能会和其他库冲突。最好使用 CDLL
或者一个私有加载器,比如 cdll = LibraryLoader(CDLL)
。
另外,cdll.LoadLibrary
返回的是一个未缓存的 CDLL
实例。没有必要在使用 CDLL
时去调用它。