不同操作系统上的python ctypes问题

2024-09-29 21:52:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试转换C函数以供Python3.6使用。在

代码如下:

lib = ctypes.WinDLL('ftrScanAPI.dll') # provided by fingerprint scanner
class FTRSCAN_IMAGE_SIZE(ctypes.Structure):
    _fields_ = [
    ("nWidth", ctypes.c_int),
    ("nHeight", ctypes.c_int),
    ("nImageSize", ctypes.c_int)
]

print('Open device and get device handle...')
hDevice = lib.ftrScanOpenDevice()
print('handle is', hDevice)
print('Get image size...')
Image_size = FTRSCAN_IMAGE_SIZE(0, 0, 0)
if lib.ftrScanGetImageSize(hDevice, ctypes.byref(Image_size)):
    print('Get image size succeed...')
    print('  W', Image_size.nWidth)
    print('  H', Image_size.nHeight)
    print('  Size', Image_size.nImageSize)
else:
    print('Get image size failed...')

功能定义:

^{pr2}$

但是使用相同代码的不同操作系统似乎有不同的结果:

  • 在Windows 7 64位
    output1

  • 在Windows 10 64位
    我不印“把手在这里” output2

    我所做的:
    因此,根据某些类型,restype可能不会显式地分配堆栈并导致溢出。在


  • Tags: 代码imagesizegetlibctypesintprint
    3条回答

    问题似乎很简单,设备句柄是一个64位实体(指向void的typedef指针)。它能在Windows7中正常工作只是一种侥幸,因为手柄的上33位是正确的零。在

    ctypes中所有函数的返回类型默认为32位int。现在在Windows10中,似乎设置了第32位(符号位),当强制为int的句柄被推到64位堆栈上进行函数调用时,会在某个地方产生符号扩展。结果地址设置了所有高位(0xFFFFFFFFA…),指向内核空间而不是用户空间。在

    因此,也许您可以使用

    lib.ftrScanOpenDevice.restype = c_void_p
    

    这并不是说您不应该为所有函数定义参数和返回类型-您应该这样做,否则它们将只遵循C语言的默认参数提升规则,并且可以工作。。。或者完全失败,这取决于函数的原型本身是否与默认参数提升兼容。例如,任何接受floats作为参数的函数都不能在没有定义原型的情况下正确调用。在

    {

    使用ctypes时,始终打开[Python 3.Docs]: ctypes - A foreign function library for Python。在

    我找到了[GitHub]: erikssm/futronics-fingerprint-reader - (master) futronics-fingerprint-reader/ftrScanAPI.h(我不知道它与您当前拥有的有多大不同,但是您目前发布的内容似乎匹配),我对您的代码做了一些更改:

    • 为函数定义argtypesrestype
    • 定义缺失类型(仅为清晰起见)
    • 其他一些不重要的更改(重命名)
    • 在上面的文件中,我还注意到了一个#pragma pack(push, 1)宏(查看[MS.Docs]: pack获取更多详细信息)。对于这个结构,它没有什么区别(感谢@AnttiHaapala的提示),因为3int4字节)成员对齐方式没有改变,但是对于其他结构(具有“更小”成员类型(例如charshort)您可能需要添加:_pack_ = 1

    你修改过的代码(不用说,我没有运行它,因为我没有.dll):

    from ctypes import wintypes
    
    # ...
    
    lib = ctypes.WinDLL('ftrScanAPI.dll') # provided by fingerprint scanner
    class FTRSCAN_IMAGE_SIZE(ctypes.Structure):
        # _pack_ = 1
        _fields_ = [
            ("nWidth", ctypes.c_int),
            ("nHeight", ctypes.c_int),
            ("nImageSize", ctypes.c_int),
        ]
    
    PFTRSCAN_IMAGE_SIZE = ctypes.POINTER(FTRSCAN_IMAGE_SIZE)
    FTRHANDLE = ctypes.c_void_p
    
    print('Open device and get device handle...')
    
    lib.ftrScanOpenDevice.argtypes = []
    lib.ftrScanOpenDevice.restype = FTRHANDLE
    
    h_device = lib.ftrScanOpenDevice()
    print('handle is', h_device)
    print('Get image size...')
    image_size = FTRSCAN_IMAGE_SIZE(0, 0, 0)
    
    lib.ftrScanGetImageSize.argtypes = [FTRHANDLE, PFTRSCAN_IMAGE_SIZE]
    lib.ftrScanGetImageSize.restype = wintypes.BOOL
    
    if lib.ftrScanGetImageSize(h_device, ctypes.byref(image_size)):
        print('Get image size succeed...')
        print('  W', image_size.nWidth)
        print('  H', image_size.nHeight)
        print('  Size', image_size.nImageSize)
    else:
        print('Get image size failed...')
    

    您应该显示.argtypes和.restype尝试,但请尝试一下:

    from ctypes import wintypes as w
    
    class FTRSCAN_IMAGE_SIZE(ctypes.Structure):
        _fields_ = [('nWidth', ctypes.c_int),
                    ('nHeight', ctypes.c_int),
                    ('nImageSize', ctypes.c_int)]
    
    FTRHANDLE = ctypes.c_void_p
    
    lib = ctypes.WinDLL('ftrScanAPI.dll')
    lib.ftrScanOpenDevice.argtypes = None
    lib.ftrScanOpenDevice.restype = FTRHANDLE
    lib.ftrScanGetImageSize.argtypes = FTRHANDLE,ctypes.POINTER(FTRSCAN_IMAGE_SIZE)
    lib.ftrScanGetImageSize.restype = w.BOOL
    

    检查WinDLLCDLL的用法。如果Python的Python是64位的,这并不重要,但是对于32位Python来说,它会有所不同。如果函数使用C调用约定(u cdecl),则使用CDLL;如果函数使用u stdcall调用约定,则使用WinDLL。如果头文件不清除,则默认值通常为u cdecl。编辑:在另一个答案的API链接中,应该使用__stdcall和{}。在

    相关问题 更多 >

      热门问题