如何将ctypes指针强制转换为Python类的实例

时间:2018-08-28 08:31:41

标签: python python-3.x ctypes

假设您有以下C代码:

typedef void (*PythonCallbackFunc)(void* userData);

void cb(PythonCallbackFunc pcf, void* userData)
{
    pcf(userData);
}

以及以下Python 3代码:

import ctypes

class PythonClass():
    def foo():
        print("bar")

CALLBACK_TYPE = ctypes.CFUNCTYPE(None, ctypes.c_void_p)

def callback(userData):
    instanceOfPythonClass = ???(userData) # <-- this part right here
    instanceOfPythonClass.foo()

lib = ctypes.cdll.LoadLibrary("path/to/lib.dll")

pc = PythonClass()

lib.cb(ctypes.byref(pc), CALLBACK_TYPE(callback))

其中“ path / to / lib.dll”是C代码的编译二进制文件。

如何将“回调”中的userData参数转换回PythonClass实例,以便可以调用函数“ foo()”?

1 个答案:

答案 0 :(得分:2)

基于[Python 3.Docs]: ctypes - A foreign function library for Python,我对您的代码进行了一些更改以使其正常工作。

dll.c

#include <stdio.h>

#if defined(_WIN32)
#  define DLL_EXPORT __declspec(dllexport)
#else
#  define DLL_EXPORT
#endif

#define C_TAG "From C"
#define PRINT_MSG_0() printf("%s - [%s] (%d) - [%s]\n", C_TAG, __FILE__, __LINE__, __FUNCTION__)


typedef void (*PythonCallbackFuncPtr)(void *userData);


DLL_EXPORT void callPython(PythonCallbackFuncPtr callbackFunc, void *userData)
{
    PRINT_MSG_0();
    callbackFunc(userData);
}

code.py

import sys
from ctypes import CDLL, CFUNCTYPE, \
    py_object


CallbackFuncType = CFUNCTYPE(None, py_object)

dll_dll = CDLL("./dll.dll")
call_python_func = dll_dll.callPython
call_python_func.argtypes = [CallbackFuncType, py_object]


class PythonClass():
    def foo(self):
        print("Dummy Python method")


def callback(userData):
    print("From Python: {:s}".format(callback.__name__))
    userData.foo()


def main():
    instance = PythonClass()
    call_python_func(CallbackFuncType(callback), instance)


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

注释

  • 在处理 Python 类型时,请使用ctypes.py_object(是 PyObject 的包装),而不要使用{{1} }
  • 始终为您从 Python <调用的 C 函数定义 argtypes (和 restype ) / em>(例如 call_python_func (包装 callPython ))
  • ctypes.c_void_p缺少1 st self )参数,因此只是在 PythonClass 内部定义的一个函数,而不是方法
  • 是否进行了其他非关键性更改(主要是重命名)

输出

PythonClass.foo()