проблема с типами Python в разных ОС - PullRequest
0 голосов
/ 07 ноября 2018

Я пытаюсь преобразовать функцию 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);

Но разные ОС с одинаковым кодом , похоже, имеют разные результаты:

В Windows 7 64 бит
output1

В Windows 10 64 бит
Я не печатаю "ручка здесь" output2

Что я пробовал:
Согласно некоторым ответам о переполнении стека, это может быть вызвано тем, что я не назначил функции argtypes и restype явно, поэтому я попытался и потерпел неудачу.

Ответы [ 3 ]

0 голосов
/ 07 ноября 2018

В 99% случаев причиной являются несоответствия между типами аргументов (и / или возвращаемых) ( [SO]: Python ctypes cdll.LoadLibrary, создать экземпляр объекта, выполнить его метод, адрес частной переменной усекается (ответ @ CristiFati) является одним из таких примеров).

Всегда иметь [Python 3.Docs]: ctypes - библиотека сторонних функций для Python , открытая при работе с ctypes .

Я нашел [GitHub]: erikssm / futronics-fingerprint reader - (мастер) futronics-fingerprint-reader / ftrScanAPI.h (я не знаю, насколько он отличается от того, что у вас сейчас есть, но вещи, которые вы опубликовали, похоже, совпадают), и я внес некоторые изменения в ваш код:

  • Определить argtypes и restype для функций
  • Определение отсутствующих типов (только для ясности)
  • Некоторые другие незначительные изменения (переименования)
  • Еще одна вещь, которую я заметил в приведенном выше файле, - это макрос #pragma pack(push, 1) (проверьте [MS.Docs]: pack для более подробной информации). Для этой структуры это не имеет значения (спасибо @AnttiHaapala за подсказку), так как выравнивание элементов 3 int ( 4 byte) не изменяется, но для других структур (с помощью " меньшие типы элементов (например, 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...')
0 голосов
/ 07 ноября 2018

Проблема, по-видимому, заключается в том, что дескриптор устройства является 64-битным объектом (указатель typedef на void). То, что это работает в вашей Windows 7, было просто случайностью, поскольку верхние 33 бита дескриптора были правильно равны нулю.

Тип возвращаемого значения всех функций в ctypes по умолчанию 32-битный int. Теперь в Windows 10 кажется, что установлен 32-й бит (знаковый бит), что вызывает расширение знака где-нибудь, когда handle-coerced-to-int помещается в 64-битный стек для вызова функции. В результирующем адресе установлены все верхние биты (0xFFFFFFFFA ...), которые указывают на пространство ядра, а не на пространство пользователя.

Таким образом, возможно, вы можете заставить ваш код "работать" всего за

lib.ftrScanOpenDevice.restype = c_void_p

Это не означает, что вы не должны определять аргументы и возвращаемые типы для всех функций - вы должны это делать, иначе они будут следовать только правилам продвижения аргументов по умолчанию на языке Си и будут работать. ... или полностью не работают, в зависимости от того, совместим ли сам прототип функции с продвижением аргументов по умолчанию. Например, любая функция, которая принимает float s в качестве аргументов, не может быть вызвана правильно без определения прототипа.

0 голосов
/ 07 ноября 2018

Вы должны показать ваши попытки .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. Используйте CDLL, если функции используют соглашение о вызовах C (__cdecl) и WinDLL, если функции используют соглашение о вызовах __stdcall. Если заголовочный файл не понятен, по умолчанию обычно используется __cdecl. Редактировать: из ссылки API в другом ответе, это __stdcall и следует использовать WinDLL.

...