python - 如何动态调用将python方法分配给从共享库导出的C指针函数
问题描述
我想解决以下问题。共享库中的某些 C 代码将调用一个指针函数,该函数是共享库的导出符号
#include <stdio.h>
typedef void (*pCallBack)(const char *);
pCallBack one_pointer;
void errorPrint(const char * str)
{
printf("my address = %p", one_pointer);
fflush(stdout);
(*one_pointer)(str);
}
让我们将该库编译为共享库
gcc -fPIC -g -shared -o call_back.so call_back.c
现在我想从 Python 调用它,动态地重新定义pCallBack
指针的实现
import ctypes
import sys
def myCallBack(str):
print("inside python" % str)
sys.stdout.flush()
libcallback = ctypes.CDLL("./call_back.so")
pointer_CallBack = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p)
libcallback.one_pointer = pointer_CallBack(myCallBack)
libcallback.errorPrint(b"one_string")
不幸的是,似乎无法重新定义导出的数据指针。我也尝试in_dll
了功能,ctypes
但它不能解决问题
当尝试运行我的 Python 脚本时,我得到以下信息
LD_PRELOAD="./call_back.so" python3 error.py
my address = (nil)[1] 2871869 segmentation fault (core dumped)
有谁知道这个问题?
解决方案
清单[Python.Docs]:ctypes - Python 的外部函数库。
使用ct.in_dll修改.dll中的变量适用于简单类型 ( int )。我不知道如何(干净地)为(函数)指针做到这一点。因此,您需要一个简单地设置回调的新函数。
还有一些错误:
- errorPrint中没有NULL检查
- 未为导出的函数设置argtypes和restype 。有关 [SO]的更多详细信息:通过 ctypes 从 Python 调用的 C 函数返回不正确的值(@CristiFati 的回答)
- 返回void的回调函数(不是void*)
这是一个工作示例。
dll00.c:
#include <stdio.h>
#if defined(_WIN32)
# define DLL00_EXPORT_API __declspec(dllexport)
#else
# define DLL00_EXPORT_API
#endif
typedef void (*CallbackFuncPtr)(const char*);
#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API void setCallback(CallbackFuncPtr cb);
DLL00_EXPORT_API void errorPrint(const char *str);
#if defined(__cplusplus)
}
#endif
CallbackFuncPtr callback = NULL;
void setCallback(CallbackFuncPtr cb) {
callback = cb;
}
void errorPrint(const char * str) {
printf("C - Callback: %p\n", callback);
fflush(stdout);
if (callback) {
(*callback)(str);
}
printf("C - done\n");
}
代码00.py
#!/usr/bin/env python
import sys
import ctypes as ct
DLL_NAME = "./dll00.so"
CallBackType = ct.CFUNCTYPE(None, ct.c_char_p)
def callback_func(s):
print("PY - callback arg: {:}".format(s))
sys.stdout.flush()
def main(*argv):
dll00 = ct.CDLL(DLL_NAME)
setCallback = dll00.setCallback
setCallback.argtypes = (CallBackType,)
setCallback.restype = None
errorPrint = dll00.errorPrint
errorPrint.argtypes = (ct.c_char_p,)
errorPrint.restype = None
cb = CallBackType.in_dll(dll00, "callback")
print("Original callback: {:}".format(cb))
callback = CallBackType(callback_func)
print("New callback: {:}\n".format(callback))
errorPrint(b"Before setting callback")
setCallback(callback)
errorPrint(b"After setting callback")
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.")
sys.exit(rc)
输出:
[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q067079630]> ~/sopr.sh *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages *** [064bit prompt]> ls code00.py dll00.c [064bit prompt]> gcc -shared -fPIC -o dll00.so dll00.c [064bit prompt]> ls code00.py dll00.c dll00.so [064bit prompt]> [064bit prompt]> python code00.py Python 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] 64bit on linux Original callback: <CFunctionType object at 0x7f07ef740ac0> New callback: <CFunctionType object at 0x7f07ef740e80> C - Callback: (nil) C - done C - Callback: 0x7f07efffc010 PY - callback arg: b'After setting callback' C - done Done.
推荐阅读
- arrays - 将txt文件读入sh脚本中的数组
- html - 在 div 中使用时,离子按钮不起作用
- node.js - npm 脚本找不到节点
- reactjs - 使用 React 为每个搜索结果路由到一个新页面?
- javascript - 为什么 Django JQuery Infinitescroll 不起作用(无法设置未定义的属性“无限”,未定义航点)
- python - 运行 `python -m unittest` 会改变带有覆盖的 `__name__` 的异常在回溯中打印的方式
- android - adb 通过互联网连接
- django - 如何在 djngo 中获取数据库中项目的最高 ID?
- scala - 使用 spark 和 scala 根据总时间查找每个 ID 的前 N 个游戏
- react-native - React Native OAuth2 和 REST API 身份验证流程