python - بيثون ctypes المسألة على أنظمة تشغيل مختلفة




python-3.x windows (2)

أنا أحاول تحويل وظيفة C للاستخدام بيثون 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);

لكن يبدو أن هناك أنظمة تشغيل مختلفة لها نفس الرمز :

  • على ويندوز 7 64 بت

  • على ويندوز 10 64 بت
    أنا لا أطبع "المقبض هنا"

    ما جربته:
    وفقًا لبعض الإجابات على تجاوز سعة مكدس الذاكرة المؤقتة ، فقد يكون السبب في ذلك عدم تعيين أنماط دالة وإعادة كتابة بشكل صريح ، لذلك حاولت وفشلت.


  • في 99٪ من الحالات ، التناقضات بين أنواع التعارض (و / أو الإرجاع) هي السبب ( .com/questions/52268294/… مثال واحد من هذا القبيل).

    دائما [Python 3.Docs]: ctypes - مكتبة الوظائف الأجنبية ل Python مفتوحة عند العمل مع ctypes .

    لقد وجدت [GitHub]: erikssm / futronics-fingerprint reader - (master) futronics-fingerprint-reader / ftrScanAPI.h (لا أعرف مدى اختلافها عن ما لديك حاليًا ، لكن الأشياء التي نشرتها حتى الآن تبدو متطابقة ) ، وقمت ببعض التغييرات على الكود:

    • تحديد argtypes وإعادة كتابة للوظائف
    • تحديد الأنواع المفقودة (للوضوح فقط)
    • بعض التغييرات الأخرى غير المهمة (إعادة تسمية)
    • شيء آخر لاحظته في الملف أعلاه ، هو ماكرو #pragma pack(push, 1) ( حدد [MS.Docs]: pack لمزيد من التفاصيل). بالنسبة لهذا الهيكل ، لا يحدث أي اختلاف (شكرًاAnttiHaapala على التلميح) ، حيث لا تتغير محاذاة الأعضاء 3 ( 4 بايت) ، ولكن بالنسبة إلى الهياكل الأخرى (مع أنواع الأعضاء "الأصغر" (مثل char ، باختصار )) أنت قد ترغب في إضافة: _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 بت (مؤشر typedef يجب إبطاله). كان يعمل في ويندوز 7 الخاص بك مجرد صدفة ، لأن 33 بت العلوي من المقبض كانت بشكل صحيح صفر.

    نوع الإرجاع لكافة الوظائف في أنواع ctypes إلى 32 بت int . الآن في نظام التشغيل Windows 10 ، يبدو أنه تم تعيين البت 32 (علامة البت) ، مما يتسبب في امتداد علامة في مكان ما عندما يتم الضغط على مقبض الإكراه على int في مكدس 64 بت لاستدعاء الوظيفة. يحتوي العنوان الناتج على جميع وحدات البت العليا (0xFFFFFFFFA ...) التي تشير إلى مساحة kernel وليس في مساحة المستخدم.

    وبالتالي ربما يمكنك الحصول على رمز "العمل" الخاص بك مع فقط

    lib.ftrScanOpenDevice.restype = c_void_p

    هذا لا يعني أنه لا ينبغي عليك تحديد أنواع الوسيطة والعودة لجميع الوظائف - يجب عليك ، وإلا فإنها ستتبع قواعد الترويج للوسيطة الافتراضية فقط للغة C ، والعمل ... أو تفشل تمامًا في العمل ، وفقًا حول ما إذا كان النموذج الأولي للوظيفة نفسها متوافق مع الترقيات الوسيطة الافتراضية. على سبيل المثال ، لا يمكن استدعاء أي وظيفة تقبل float s كوسائط بشكل صحيح دون تعريف النموذج الأولي.





    ctypes