当前位置: 首页 > 面试题库 >

用ctypes替换共享库中的函数指针

姜博
2023-03-14
问题内容

我试图通过ctypes用Python中定义的回调替换共享库中的现有函数指针。

C中共享库的源代码:

#include <assert.h>
#include <stdio.h>

void (*plot)();

int c_main(int argc, void** argv) {
  printf("plot is %p\n", (void*)plot);
  assert(plot != NULL);
  plot();
  return 0;
}

Python脚本的来源:

from sys import platform
from pathlib import Path
import ctypes
import _ctypes


FUNCTYPE = ctypes.WINFUNCTYPE if platform == 'win32' else ctypes.CFUNCTYPE


def dlclose(obj):
    if platform == "win32":
        _ctypes.FreeLibrary(obj._handle)
    else:
        _ctypes.dlclose(obj._handle)


def enc_args(args):
    C_ARGS = (ctypes.POINTER(ctypes.c_char) * len(args))()
    for idx, arg in enumerate(args):
        C_ARGS[idx] = ctypes.create_string_buffer(arg.encode("utf-8"))
    return C_ARGS


@FUNCTYPE(None)
def plotxy():
    print("plotxy")


C_ARGS = enc_args([str(Path(__file__))])
CAUXDLL = ctypes.CDLL("./test.so")

print(plotxy)

print(CAUXDLL.plot)

CAUXDLL.plot = plotxy

print(CAUXDLL.plot)

print(CAUXDLL.c_main(len(C_ARGS), C_ARGS))

测试它的脚本:

gcc -fPIC -shared -o test.so test.c
python3 test.py

我得到的输出:

# ./test.sh
<CFunctionType object at 0x7fb1f0abb1c0>
<_FuncPtr object at 0x7fb1f0abb640>
<CFunctionType object at 0x7fb1f0abb1c0>
plot is (nil)
python3: test.c:8: c_main: Assertion `plot != NULL' failed.
./test.sh: line 3: 21171 Aborted                 python3 test.py

因此,似乎在Python(plotxy)中定义的函数是type CFunctionType,而在C中定义的函数指针是type
_FuncPtr。尽管应用了替换in CAUXDLL,但在调用main函数时似乎没有任何效果。

除了阅读https://docs.python.org/3/library/ctypes.html#module-
ctypes之外
,我还发现了其他问题(例如,如何在ctypes或python
cytpes中使用typedef创建回调函数-
分段错误(核心已转储)),但找不到如何将CFunctionType(plotxy)转换为_FuncPtr

编辑

我相信这可能与ctypes的常规用法无关。这是我已经成功实现的,并且在文档中对此进行了充分的说明。这个问题超越了。我不想执行C函数。我希望Python用Python编写的回调替换现有的函数指针。请注意,可以通过使用辅助C函数来做到这一点(请参阅https://github.com/ghdl/ghdl-
cosim/blob/master/vhpidirect/shared/pycb/caux.c#L32-L40)。因此,这个问题是关于如何在没有辅助功能的情况下实现它(如果可能)。


问题答案:

从中访问全局变量的方法ctypes是使用in_dll,但是似乎没有公开的方法来更改函数指针。我只能阅读和调用它,所以我认为没有帮助程序功能是不可能的。

以下示例更改了int全局变量,但是CFUNCTYPE实例没有value成员可以对其进行更改。我添加了一个C帮助程序来设置全局值以解决该问题,并添加一个回调的默认值,以在更改它之前验证它是否已正确访问。

test.c:

#include <stdio.h>

#define API __declspec(dllexport)

typedef void (*CB)();

void dllplot() {
    printf("default\n");
}

API CB plot = dllplot;
API int x = 5;

API int c_main() {
  printf("x=%d (from C)\n",x);
  plot();
  return 0;
}

API void set_cb(CB cb) {
    plot = cb;
}

test.py:

from ctypes import *

PLOT = CFUNCTYPE(None)

dll = CDLL('./test')
dll.c_main.argtypes = ()
dll.c_main.restype = c_int
dll.set_cb.argtypes = PLOT,
dll.set_cb.restype = None

@PLOT
def plotxy():
    print("plotxy")

x = c_int.in_dll(dll,'x')
plot = PLOT.in_dll(dll,'plot')
print(f'x={x.value} (from Python)')
x.value = 7
print('calling plot from Python directly:')
plot()
print('calling c_main():')
dll.c_main()
dll.set_cb(plotxy)
print('calling plot from Python after setting callback:')
plot()
print('calling plot from C after setting callback:')
dll.c_main()

输出:

x=5 (from Python)
calling plot from Python directly:
default
calling c_main():
x=7 (from C)
default
calling plot from Python after setting callback:
plotxy
calling plot from C after setting callback:
x=7 (from C)
plotxy

请注意,全局指针用于.contents访问其值,因此我尝试使用aPOINTER(CFUNCTYPE(None))和using,plot.contents = plotxy但未正确分配全局变量,C崩溃了。

我什至尝试将实际的全局指针添加到函数指针:

API CB plot = dllplot;
API CB* pplot = &plot;

然后使用:

PLOT = CFUNCTYPE(None)
PPLOT = POINTER(PLOT)
plot = PPLOT.in_dll(dll,'pplot')
plot.contents = plotxy

这使我可以通过分配函数.contents,但c_main仍称为默认绘图值。因此CFUNCTYPE,除了功能参数之外,用作任何功能的功能似乎都没有实现。



 类似资料:
  • 问题内容: 我正在从python脚本中调用一个so文件。据我了解,我真的不需要释放使用ctypes在python中打开的共享库。但是,在我的so文件代码中,它dlopen另一个so文件并且不执行dlclose()。在这种情况下,从python端使用安全吗?我不必释放在ctypes内部加载的共享库soe文件吗? 问题答案: 始终遵循 “自己清洁后清理 ”的规则(尽管现代技术会为您提供清洁方面的帮助)

  • 问题内容: 我想使用ctypes在库中映射一个声明为全局值的int值。 目前,我可以像这样加载库: 并成功映射了许多功能。但是,出于错误检查的目的,其中许多设置了变量,因此我也需要访问该变量。但是,如果我尝试访问它,则会得到: 当然,它不是函数指针,尝试调用它会导致段错误。 在主头文件和API头文件中声明为。 Objdump将符号显示为: 问题答案: ctypes文档中有一节关于访问dll中导出的

  • 问题内容: 从共享库/ dll调用函数的最简单,最安全的方法是什么?我对在Linux上执行此操作最感兴趣,但是如果有一种与平台无关的方法会更好。 有人可以提供示例代码来显示如何在用户将自己的版本编译到共享库的情况下执行以下工作吗? 顺便说一句,我知道如何编译共享库(),我只需要知道一种在运行时加载它的简单方法。 问题答案: 注意: 您正在库调用周围传递C 对象(在这种情况下为STL字符串)。有 *

  • 我正在尝试开发共享库,目录结构如下 src/com/mycomapny 我的测试。棒极了 测试。棒极了 我的Jenkinsfile调用test.groovy中唯一可用的方法,需要输入。它导入MyTest并创建对象,调用构造函数,然后是执行MyTest.groovy文件中可用功能的实际方法 在这里,构造函数类从未从全局vars/test调用过。棒极了 我尝试从groovy调用类和方法,它工作正常,但

  • 问题内容: 如何在gcc中静态链接共享库函数? 问题答案: 参考: http://www.linuxquestions.org/questions/linux-newbie-8/forcing-static- linking-of-shared- libraries-696714/ http://linux.derkeiler.com/Newsgroups/comp.os.linux.develo

  • 问题内容: 我正在使用一个包含单个调用的.dll,该调用返回一个函数指针数组。GetMyApi()返回指向结构的指针,该结构是函数指针的数组。函数本身具有不同的单独输入和输出。到目前为止我尝试过的是: 我无法轻易更改的C代码: C中的代码: { int (__cdecl IsValidInt)(int i); int (__cdecl InvalidInt)(); int (__cdecl *Is