如何将 ctypes 指针转换为 Python class 的实例

How to cast a ctypes pointer to an instance of a Python class

假设您有以下 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 代码的编译二进制文件。

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

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

dll00.c:

#include <stdio.h>

#if defined(_WIN32)
#  define DLL00_EXPORT __declspec(dllexport)
#else
#  define DLL00_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);


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

code00.py:

#!/usr/bin/env python

import sys
from ctypes import CDLL, CFUNCTYPE, \
    py_object


CallbackFuncType = CFUNCTYPE(None, py_object)

dll = CDLL("./dll00.dll")
callPython = dll.callPython
callPython.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()
    callPython(CallbackFuncType(callback), instance)


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

备注:

  • 在处理 Python 类型时,使用 ctypes.py_object (它是 PyObject) 而不是 ctypes.c_void_p
  • AlwaysC[定义 argtypes(和 restype) =51=] 您从 Python 调用的函数(例如 call_python_func (包装 call Python))。检查 了解更多详情
  • PythonClass.foo 缺少 1st (self) 参数和因此只是 PythonClass 中定义的函数,而不是方法
  • 是否进行了其他非关键更改(主要是重命名)

输出:

(py35x64_test) e:\Work\Dev\Whosebug\q052053434>"c:\Install\x86\Microsoft\Visual Studio Community15\vc\vcvarsall.bat" x64

(py35x64_test) e:\Work\Dev\Whosebug\q052053434>dir /b
code00.py
dll00.c

(py35x64_test) e:\Work\Dev\Whosebug\q052053434>cl /nologo /DDLL dll00.c  /link /DLL /OUT:dll00.dll
dll.c
   Creating library dll00.lib and object dll00.exp

(py35x64_test) e:\Work\Dev\Whosebug\q052053434>dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj

(py35x64_test) e:\Work\Dev\Whosebug\q052053434>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.py
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32

From C - [dll.c] (18) - [callPython]
From Python: callback
Dummy Python method