我正在尝试使用ctypes包装一个C库。该库的一个功能是ondestroy回调,当该库返回的句柄即将被销毁时会调用该回调。
回调具有签名:
void cb(f *beingdestroyed);
Run Code Online (Sandbox Code Playgroud)
void *当库返回API时,该API允许将用户指定的内容与f 关联。因此,我可以关联用于将其包装为用户数据的py_object。我的计划是要有一个is_valid字段,当触发回调以提取user_data并将此字段设置为false时。
我的问题是如何着手提取我的高级知识py_object;我可以将用户数据转换为a ctypes.void_p并转换为a,ctypes.py_object但是随后我只有Python C API可以使用。可以通过写入将内容转发到我可以使用的高级对象user_object.is_valid = 0。
详细阐述托马斯·海勒的答案:
c_void_p为context参数指定py_object为context参数指定py_object(my_python_context_object)cast(context, py_object).value这是一个工作示例。从一个简单的DLL的C源代码开始:
// FluffyBunny.c
// Compile on windows with command line
// cl /Gd /LD FluffyBunny.c
// Result is FluffyBunny.DLL, which exports one function:
// FluffyBunny() uses __cdecl calling convention.
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID) {
return TRUE;
}
typedef int (*FLUFFYBUNNY_CALLBACK)(void *context);
__declspec(dllexport) int FluffyBunny(FLUFFYBUNNY_CALLBACK cb, void *context) {
int result = 0;
int count = 0;
if (cb) {
while (result == 0) {
result = (*cb)(context);
++count;
}
}
return count;
}
Run Code Online (Sandbox Code Playgroud)
这是一个调用DLL的Python程序:
# FluffyBunny.py
from ctypes import *
# Declare a class that will be used for context info in calls to FluffyBunny()
class Rabbit:
def __init__(self):
self.count = 0
# FluffyBunny() wants a callback function with the following C prototype:
# typedef int (*FLUFFYBUNNY_CALLBACK)(void *context);
FLUFFYBUNNY_CALLBACK = CFUNCTYPE(c_int, c_void_p)
# This DLL has been compiled with __cdecl calling convention.
FluffyBunny_dll = CDLL('FluffyBunny.dll')
# Get the function from the library. Its C prototype is:
# int FluffyBunny(FLUFFYBUNNY_CALLBACK cb, void *context);
# Note that I use "py_object" instead of "c_void_p" for the context argument.
FluffyBunny = FluffyBunny_dll.FluffyBunny
FluffyBunny.restype = c_int
FluffyBunny.argtypes = [FLUFFYBUNNY_CALLBACK, py_object]
# Create Python version of the callback function.
def _private_enumerateBunnies(context):
# Convert the context argument to a py_object, and extract its value.
# This gives us the original Rabbit object that was passed in to
# FluffyBunny().
furball = cast(context, py_object).value
# Do something with the context object.
if furball:
furball.count += 1
print 'furball.count =', furball.count
# Return non-zero as signal that FluffyBunny() should terminate
return 0 if (furball.count < 10) else -1
else:
return -1
# Convert it to a C-callable function.
enumerateBunnies = FLUFFYBUNNY_CALLBACK(_private_enumerateBunnies)
# Try with no context info.
print 'no context info...'
result = FluffyBunny(enumerateBunnies, None)
print 'result=', result
# Give it a Python object as context info.
print 'instance of Rabbit as context info...'
furball = Rabbit()
result = FluffyBunny(enumerateBunnies, py_object(furball))
print 'result=', result
Run Code Online (Sandbox Code Playgroud)