调用的 Ctypes 参数没有足够的参数

Ctypes arguments called with not enough arguments

我正在尝试使用 ctypes 导入和使用 c++ dll 的功能。使用 windll 成功调用该函数,但在传递参数时继续显示 ValueError: Procedure probably called with not enough arguments (4 bytes missing)。我排除了所有可能性并且非常确定我使用了正确的调用约定。更改为 oledll 或 cdll 也无济于事。下面是代码,也是dll函数调用的使用说明。谢谢

dll user manual

代码:

from ctypes import *

biometric = windll.LoadLibrary(r"G:\software\datalite\OnLineInterface.dll")

i = c_long(1)
biometric.OnLineGetData.argtypes = [c_long,c_long,POINTER(c_long)]
b = pointer(i)
biometric.OnLineGetData(c_long(1),c_long(1),b)

所以,它来自 Biometrics' DataLINK API 这也是记录在 [NI.Forums]: Manual1.pdf。根据那个(以及问题中的图像),函数原型是:

int __stdcall OnLineGetData(long channel, long sizeMsToRead, SAFEARRAY **DataArray, long *pActualSamples); 

因此,您缺少 3rd 参数(双指针:SAFEARRAY **DataArray)。不幸的是,这“有点”复杂(您跳过它的一个可能原因:))。
我准备了一个小的(和虚拟的)示例(我还包括了 SAFEARRAY 创建,但是有部分(sample_rate) 需要澄清它才能正常工作,而且,我没有测试它)。

code00.py:

#!/usr/bin/env python

import sys
import ctypes as ct
from ctypes import wintypes as wt


class SAFEARRAYBOUND(ct.Structure):
    _fields_ = [
        ("cElements", wt.ULONG),
        ("LONG", wt.LONG),
    ]


class SAFEARRAY(ct.Structure):
    _fields_ = [
        ("cDims", wt.USHORT),
        ("fFeatures", wt.USHORT),
        ("cbElements", wt.ULONG),
        ("cLocks", wt.ULONG),
        ("pvData", ct.c_void_p),
        ("rgsabound", SAFEARRAYBOUND * 1),
    ]

PSAFEARRAY = ct.POINTER(SAFEARRAY)
PPSAFEARRAY = ct.POINTER(PSAFEARRAY)


def main(*argv):
    mod_name = r"G:\software\datalite\OnLineInterface.dll"
    #mod_name = "kernel32"
    OnLineInterfaceDll = ct.WinDLL(mod_name)
    OnLineGetData = OnLineInterfaceDll.OnLineGetData
    OnLineGetData.argtypes = (ct.c_long, ct.c_long, PPSAFEARRAY, ct.POINTER(ct.c_long))
    OnLineGetData.restype = ct.c_int

    OleAut32Dll = ct.WinDLL("OleAut32.dll")
    SafeArrayDestroy = OleAut32Dll.SafeArrayDestroy
    SafeArrayDestroy.argtypes = (PSAFEARRAY,)
    SafeArrayDestroy.restype = ct.c_long
    # Not quite sure how to initialize the SAFEARRAY, you'll have to search for C examples doing that.
    # There is SafeArrayCreate function, but given the double pointer, I think that's called from within OnLineGetData
    # However, I assume that freing the pointer is your responsibility (hence SafeArrayDestroy).

    channel = 1
    millis = 1
    samples = ct.c_long(-1)

    create_array = 1
    if create_array:
        # Create the array according to (available) docs. Needless to say that I didn't test it
        print("Creating array")
        SafeArrayCreateVector = OleAut32Dll.SafeArrayCreateVector
        SafeArrayCreateVector.argtypes = (ct.c_ushort, wt.LONG, wt.ULONG)
        SafeArrayCreateVector.restype = PSAFEARRAY

        VT_I2 = 2
        sample_rate = 5  # !!! PLACE AN APPROPRIATE VALUE (GOT FROM THE DEVICE) HERE !!!
        psa = SafeArrayCreateVector(VT_I2, 0, millis * sample_rate)
        #print(psa)
        ppsa = ct.pointer(psa)
        #print(ppsa)
    else:
        print("Using dummy array")
        ppsa = PPSAFEARRAY()  # Dummy double pointer

    res = OnLineGetData(channel, millis, ppsa, ct.byref(samples))
    print("\n{0:s} returned: {1:d}".format(OnLineGetData.__name__, res))
    if ppsa:
        print("Doing smth with the data:", ppsa.contents)
        SafeArrayDestroy(ppsa.contents)


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main(*sys.argv[1:])
    print("\nDone.")