【问题标题】:Assigning Python function to ctypes pointer variable将 Python 函数分配给 ctypes 指针变量
【发布时间】:2013-02-16 02:13:21
【问题描述】:

我将以下 C 源代码编译成 DLL:

int (*pfuncExtB)(int a, int b);

int funcB(int a, int b)
{
    return funcExtB(a, b);
}

int funcExtB(int a, int b)
{
    return pfuncExtB(a, b);
}

我想做的是让 pfuncExtB “指向”一个 Python 函数,所以这就是我在 Python 中所做的:

from ctypes import *

def add(a, b):
    return a + b

mutdll = cdll.LoadLibrary("my.dll")

pfuncExtB = (POINTER(CFUNCTYPE(c_int, c_int, c_int))).in_dll(mutdll, 'pfuncExtB')

funcB = mutdll.funcB
funcB.argtypes = [c_int, c_int]
funcB.restype = c_int

pfuncExtB.contents = CFUNCTYPE(c_int, c_int, c_int)(add)

print funcB(3 , 4)

在此之后,我希望以下调用返回 7

print funcB(3, 4)

但我明白了:

Traceback (most recent call last):
..................
print funcB(3, 4)
WindowsError: exception: access violation reading 0x00000001

那么我在这里做错了什么?是否可以将 Python 函数“分配”给 ctypes 函数指针变量?

编辑:在看到 Mark Tolonen 的解决方法(用 C 编写的用于指向函数变量的指针的集合函数)后,我发现为什么在尝试时它对我不起作用。

这不起作用:

set(CFUNCTYPE(c_int,c_int,c_int)(add))
print funcB(2, 3)

虽然这有效:

callback = CFUNCTYPE(c_int,c_int,c_int)(add)
set(callback)
print funcB(2, 3)

其中 set 是一个 C 函数,它将指向函数的参数分配给全局,就像 Mark 的回答一样。正如他指出的那样,答案就在文档中:

回调函数的重要说明: 确保您保留对 CFUNCTYPE() 对象的引用,只要它们是从 C 代码中使用的。 ctypes 不会,如果您不这样做,它们可能会被垃圾回收,从而在进行回调时使您的程序崩溃。

【问题讨论】:

标签: python-2.7 ctypes


【解决方案1】:

Python 中全局变量的正确类型是CFUNCTYPE(c_int,c_int,c_int)(不是POINTER()),但是我看不到有一种方法可以更改该变量的值。如果你可以添加一个 set 函数,它就可以工作:

C

typedef int (*FUNC)(int,int);

__declspec(dllexport) FUNC pfuncExtB;

__declspec(dllexport) void set(FUNC f)
{
    pfuncExtB = f;
}

int funcExtB(int a, int b)
{
    return pfuncExtB(a, b);
}

__declspec(dllexport) int funcB(int a, int b)
{
    return funcExtB(a, b);
}

Python

from ctypes import *

FUNC = CFUNCTYPE(c_int,c_int,c_int)

@FUNC
def add(a, b):
    return a + b

mutdll = cdll.LoadLibrary('x')

mutdll.set.argtypes = [FUNC]
mutdll.set.restype = None

mutdll.set(add) # set the global variable

pfuncExtB = FUNC.in_dll(mutdll,'pfuncExtB')
print(pfuncExtB(1,2)) # -> 3

funcB = mutdll.funcB
funcB.argtypes = [c_int, c_int]
funcB.restype = c_int

print(funcB(3 , 4)) # -> 7

请注意,这不起作用:

pfuncExtB = POINTER(FUNC).in_dll(mutdll,'pfuncExtB')
pfuncExtB.contents(1,2) # exception!

【讨论】:

  • 感谢您的意见。事实上,我自己考虑过你的解决方法,但我没有为我工作!看到您的回答后我再次尝试,发现我的“设置”功能有问题,请参阅我的编辑
  • 您原来的set 版本不起作用,因为在set 调用完成后传递给它的CFUNCTYPE 对象没有引用,所以它被销毁了。在您的第二个版本中将其分配给callback 会保留对它的引用。请参阅文档中16.17.1.17 部分的最后一段。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-24
  • 1970-01-01
相关资源
最近更新 更多