Python Ctypes problema en diferentes sistemas operativos

Estoy tratando de convertir la función C para Python 3.6 uso.

código como abajo:

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...') 

definición de la función:

 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); 

Pero diferentes sistemas operativos con el mismo código parecen tener un resultado diferente:

  • En Windows 7 64 bit
    salida1
  • En Windows 10 64 bit
    No imprimo “la manija está aquí” salida2
  • Lo que he intentado:
    De acuerdo con algunas respuestas en el desbordamiento de stack, esto puede deberse a que no se asignan los tipos de función y se vuelven a escribir explícitamente, así que lo intenté y fracasé.

    En el 99% de los casos, las inconsistencias entre argumentos (y / o retorno) son inconsistencias de tipo ( [SO]: Python ctypes cdll.LoadLibrary, ejemplificar un objeto, ejecutar su método, la dirección de la variable privada truncada (respuesta de @ CristiFati) es un ejemplo de ello).

    Siempre tenga [Python 3]: ctypes – Una biblioteca de funciones extrañas para Python abierta cuando se trabaja con ctypes .

    Encontré [GitHub]: erikssm / futronics-fingerprint-reader – (master) futronics-fingerprint-reader / ftrScanAPI.h (No sé qué tan diferente es de lo que tienes actualmente, pero las cosas que publicaste parecen coincidir ), e hice algunos cambios a su código:

    • Definir argtypes y reescribir para funciones.
    • Definir los tipos que faltan (solo por claridad)
    • Algunos otros cambios insignificantes (nombres)
    • Otra cosa que noté en el archivo anterior, es una macro #pragma pack(push, 1) (verifique [MSDN]: paquete para obtener más detalles). Para esta estructura no hace ninguna diferencia (gracias a @AnttiHaapala por la sugerencia), ya que la alineación de los 3 int ( 4 bytes) no cambia, pero para otras estructuras (con tipos de miembros “más pequeños” (por ejemplo, char , short )) Es posible que desee agregar: _pack_ = 1

    Su código modificado (no hace falta decirlo, no lo ejecuté porque no tengo el .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...') 

    El problema parece ser simplemente que el identificador del dispositivo es una entidad de 64 bits (un puntero de tipo typedef’d para anular). El hecho de que funcione en su Windows 7 fue solo una casualidad, ya que los 33 bits superiores del identificador eran correctamente cero.

    El tipo de retorno de todas las funciones en ctypes se establece de forma predeterminada en int 32 bits. Ahora, en Windows 10, parece que el bit 32 (bit de signo) se configuró, lo que provoca una extensión de signo en algún lugar cuando el identificador forzado a ingresar se empuja en la stack de 64 bits para la llamada a la función. La dirección resultante tiene todos los bits superiores establecidos (0xFFFFFFFFA …) que apunta al espacio del kernel y no en el espacio del usuario.

    Por lo tanto, tal vez pueda obtener su código “trabajando” con solo

     lib.ftrScanOpenDevice.restype = c_void_p 

    Esto no quiere decir que no deba definir los tipos de argumento y retorno para todas las funciones; debería hacerlo; de lo contrario, seguirán solo las reglas de promoción de argumentos predeterminadas del lenguaje C, y el trabajo … o no funcionará, dependiendo de sobre si el prototipo de la función es compatible con las promociones de argumentos predeterminados. Por ejemplo, cualquier función que acepte float s como argumentos no se puede llamar correctamente sin definir el prototipo.

    Debe mostrar sus bashs de .argtypes y .restype, pero intente esto:

     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 

    Compruebe el uso de WinDLL vs. CDLL . No importará si su Python es de 64 bits, pero hace una diferencia en Python de 32 bits. Use CDLL si las funciones usan la convención de llamadas C (__cdecl) y WinDLL si las funciones usan la convención de llamadas __stdcall. Si el archivo de encabezado no está claro, el valor predeterminado suele ser __cdecl. Editar: desde el enlace de la API en otra respuesta, es __stdcall y se debe usar WinDLL .