从 C/C++ 调用 python 方法,并在访问冲突异常中失败

Call a python method from C/C++, and fail in access violation exception

我们想在 python 中调用一个 c/c++ 扩展,在扩展中我们需要回调 python 脚本中的一个函数。

这是 python 代码

# -*- coding: UTF-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8') 
import ctypes

class MyClass:
    def func(self):
        print "func"
        open("out.txt","w").write("func")
        print "func2"
        return


mDll = ctypes.CDLL("xx.dll", ctypes.RTLD_GLOBAL)
c = MyClass()
# c.func() is what we wanna run below
mDll.callFunc.argtypes = [ctypes.py_object]
mDll.callFunc(c)

下面是 xx.dll 的 c 源代码,由 VS 2008 构建:

__declspec(dllexport) int callFunc(PyObject* self){
    printf("callFunc\n");
    //PyObject* ret = PyObject_CallMethod(self, "func", NULL);

    PyObject* ret2 = PyObject_CallFunctionObjArgs(PyObject_GetAttrString(self, (char*)"func") , self, NULL);

    printf("callFunc2\n");

    return 0;
}

我们尝试了两种方法(PyObject_CallFunctionObjArgs / PyObject_CallMethod),两种方法都没有调用失败。结果显示:

Traceback (most recent call last):
  File "test\tCall.py", line 19, in <module>
    mDll.callFunc(c)
WindowsError: exception: access violation writing 0x0000000C

我们怎么称呼 MyClass.func ?

ctypes 在调用使用 CDLL 加载的任何内容之前释放 GIL。你需要显式持有它,或者使用ctypes.PyDLL,或者实际上,最好只写一个普通的扩展模块而不是手动加载DLL。

此外,管理你的引用,不要将 self 传递给 func 方法;那是隐式发生的。

(我不知道这些是不是所有的问题,但这些是我注意到的。)