我是不是正确使用 ctypes 来对这个结构进行 python 化?
Posted
技术标签:
【中文标题】我是不是正确使用 ctypes 来对这个结构进行 python 化?【英文标题】:Am I using ctypes correctly to pythonify this struct?我是否正确使用 ctypes 来对这个结构进行 python 化? 【发布时间】:2014-09-13 00:32:31 【问题描述】:我正在尝试使用 python 的 ctypes 与 this DLL 交谈。许多函数采用或返回HGRABBER
类型:
typedef struct HGRABBER_t__ int unused; HGRABBER_t;
#define HGRABBER HGRABBER_t*
(完整的头文件可以查看here)。下面是一个返回 HGRABBER
类型的函数原型示例:
HGRABBER __stdcall IC_CreateGrabber();
这是我在 python 中实现此结构并使用它从 DLL 调用该函数的尝试:
import ctypes as C
class GrabberHandle(C.Structure):
_fields_ = [('unused', C.c_int)]
dll = C.windll.LoadLibrary('tisgrabber_x64.dll')
dll.create_grabber = dll.IC_CreateGrabber
dll.create_grabber.argtypes = []
dll.create_grabber.restype = GrabberHandle
my_handle = dll.create_grabber()
这似乎可行,但我担心我做错了。我没有使用 C 的经验,而且我认为我不理解定义 HGRABBER
类型的 typedef
和 #define
语句。 我是否正确调用IC_CreateGrabber
?我是否应该将 GrabberHandle 定义为指向结构的指针,而不是结构?
感谢阅读,如果我能以某种方式澄清我的问题,请告诉我。
【问题讨论】:
【参考方案1】:您说得对,您实际上想要POINTER
到Structure
,而不是Structure
本身。
将 C 翻译成英文,非常松散(如果您尝试学习 C,这种方式会很危险,但足以使用 ctypes
):
struct
将一个名为 struct HGRABBER_t__
的类型定义为一个包含一个 int
的结构。
typedef
定义了一个名为HGRABBER_t
的类型,作为struct HGRABBER_t__
的同义词。
#define
将名为 HGRABBER
的类型定义为指向 HGRABBER_t
的指针。
所以,您的GrabberHandle
相当于HGRABBER_t
; HGRABBER
的等价物是:
GrabberHandlePtr = C.POINTER(GrabberHandle)
所以你想要这个:
dll.create_grabber.restype = GrabberHandlePtr
可能很难调试差异。一个只有int
的C 结构看起来与内存中的int
相同。在 Win32 上,int
和指针都是 32 位值。一个名为 unused
的 int 很可能充满了无意义的垃圾,因此很难将它与您不小心视为 int 的指针区分开来。所以一切看起来都很好,直到您稍后在代码中段错误 30 行并且不知道出了什么问题。 :)
【讨论】:
您的答案对我来说似乎是正确的,但另一个答案与您相矛盾。通常我可以通过运行代码来判断谁是对的,但这次似乎不是,因为你提到的原因(直到后来没有崩溃)。有没有办法证明哪个答案是正确的,或者我应该在这里凭直觉走? @Andrew,未使用的int
是不必要的。重要的是指针中的地址,它可能用于 C++ 对象。在 64 位系统上将结果设置为 4 字节结构是错误的;截断指针地址。您可以改用c_void_p
,但为了更好地使用指向结构的指针的类型安全棒。您不需要定义_fields_
。你可以使用HGRABBER = POINTER(type('HGRABBER', (Structure,), ))
。
@eryksun:关于使用指向空结构的指针来表示 Win32 API 中经常使用的那种“不透明句柄”的好建议。【参考方案2】:
这个库做你想做的事:https://github.com/morefigs/py-ic-imaging-control :)
但为了回答您的问题,该库使用代码:
from ctypes import *
import os
class GrabberHandle(Structure):
pass
GrabberHandle._fields_ = [('unused', c_int)]
# set and check path
dll_path = os.path.join(os.path.expanduser('~'),
'Documents\\The Imaging Source Europe GmbH\\TIS Grabber DLL\\bin\\win32\\tisgrabber.dll')
with open(dll_path) as thefile:
pass
# open DLL
_ic_grabber_dll = windll.LoadLibrary(dll_path)
# create grabber
create_grabber = _ic_grabber_dll.IC_CreateGrabber
create_grabber.restype = POINTER(GrabberHandle)
create_grabber.argtypes = None
# get handle
handle = create_grabber()
编辑:根据 abarnert 的回答,将代码更改为使用指向 GrabberHandle
的指针,因为这是正确的。但是,在这种特殊情况下,我没有发现任何实际差异(使用 32 位 DLL),可能是因为 GrabberHandle
结构非常简单。
【讨论】:
我同意代码可以正常工作,但是当我对代码进行微不足道的修改时会出现访问冲突,我认为这可能是相关的。另外我使用的是 64 位,我认为您使用的是 32。 无关,但是当您在这里时:set_format 方法是怎么回事?如果调用它会崩溃,但是文档不是说在获取图像之前必须设置接收器格式吗? 您需要使用 64 位 DLL 吗?您使用的是 64 位 Python 吗?他们必须匹配。我正在使用 32 位 Python 和 DLL(在 64 位 Windows 7 上)。 你在set_format()
中发现了一个错字错误,我现在已经在 GitHub 上修复了。
C 作为一种弱类型语言,要求将对象指针转换为适当大小的整数,然后在使用之前将其转换回以保证安全。在您的 32 位 Python 上,int
是任何对象指针的适当大小的整数。所以,你可以逃脱惩罚。以上是关于我是不是正确使用 ctypes 来对这个结构进行 python 化?的主要内容,如果未能解决你的问题,请参考以下文章
没有为指向结构的指针调用 ctypes.Structure 子类的 ctypes __del__
如何使用 ctypes 在 python 中正确包装 C API?