我正在尝试转换C函数以便使用python 3.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...')
功能定义:
typedef struct FTR_PACKED __FTRSCAN_IMAGE_SIZE {
int nWidth;
int nHeight;
int nImageSize;
} FTRSCAN_IMAGE_SIZE, *PFTRSCAN_IMAGE_SIZE;
FTRHANDLE ftrScanOpenDevice(); # typedef void * FTRHANDLE;
BOOL ftrScanGetImageSize(FTRHANDLE ftrHandle,
PFTR_SCAN_IMAGE_SIZE pImageSize);
但具有相同代码的不同操作系统似乎有不同的结果:
在99%的情况下,参数(和/或返回)类型不一致之间的不一致是原因([SO]: Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated (@CristiFati's answer)就是这样一个例子)。
使用ctypes时始终打开[Python 3.Docs]: ctypes - A foreign function library for Python。
我找到了[GitHub]: erikssm/futronics-fingerprint-reader - (master) futronics-fingerprint-reader/ftrScanAPI.h(我不知道它与你现有的有多么不同,但到目前为止你发布的内容似乎相符),我对你的代码做了一些修改:
#pragma pack(push, 1)
宏(有关详细信息,请查看[MS.Docs]: pack)。对于这个结构它没有区别(感谢@AnttiHaapala的提示),因为3 int(4字节)成员对齐不会改变,但对于其他结构(具有“较小”成员类型(例如char,short))你可能想添加:_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...')
问题似乎只是设备句柄是一个64位实体(一个指向void的typedef指针)。它在你的Windows 7中工作只是一个侥幸,因为句柄的高33位正确为零。
ctypes中所有函数的返回类型默认为32位int
。现在在Windows 10中似乎设置了第32位(符号位),当在64位堆栈上按下handle-coerced-to-int进行函数调用时,会导致符号扩展。结果地址的所有高位都设置为(0xFFFFFFFFA ...),它指向内核空间而不是用户空间。
因此,也许你可以让你的代码“正常工作”
lib.ftrScanOpenDevice.restype = c_void_p
这并不是说你不应该为所有函数定义参数和返回类型 - 你应该,否则它们将只遵循C语言的默认参数提升规则,并且工作......或完全无法工作,具体取决于关于函数的原型本身是否与默认参数促销兼容。例如,在没有定义原型的情况下,任何接受float
s作为参数的函数都无法正确调用。
你应该显示你的.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
检查WinDLL
与CDLL
的使用。如果你的Python是64位无关紧要,但它对32位Python有所不同。如果函数使用C调用约定(__cdecl)和CDLL
函数使用__stdcall调用约定,则使用WinDLL
。如果头文件不清除,则默认值通常为__cdecl。编辑:从另一个答案的API链接,应该使用__stdcall
和WinDLL
。