【问题标题】:Ctypes arguments called with not enough arguments没有足够参数调用的 Ctypes 参数
【发布时间】:2020-10-14 12:12:37
【问题描述】:

我正在尝试通过 ctypes 导入和使用 c++ dll 的功能。使用windll成功调用该函数,但在传递参数时一直显示ValueError:过程可能调用了没有足够的参数(缺少4个字节)。我排除了所有可能性,并且很确定我使用了正确的调用约定。更改为 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)

【问题讨论】:

    标签: python dll ctypes


    【解决方案1】:

    所以,它来自 BiometricsDataLINK 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.")
    

    【讨论】:

    • 我得到了你提供的代码工作。但是,我现在对 SAFEARRAY 感到困惑,因为我阅读了有关 SAFEARRAY 的文档,确实它需要在您的代码中使用结构声明,但显然在 C 中使用 SafeArrayCreate 函数来初始化安全数组。 ctypes 和 Python 都不支持此函数,是否可以在 Python 中为 ctypes 初始化 SAFEARRAY?网上只有少数讨论。
    • 可以调用,在oleaut32.dll中。但正如我所说,我认为函数是由 OnLineGetData 在内部调用的。但是来自 CTypesSafeArrayCreate 调用可能是另一个问题的主题。
    • 更新:双指针通过函数返回布尔值false。所以我假设该函数不会为指针地址创建安全数组。
    • 也就是说,我可以理解为函数本身在传递空指针时会创建一个安全数组吗?
    猜你喜欢
    • 2022-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-04
    • 2017-03-29
    • 1970-01-01
    • 2020-06-18
    相关资源
    最近更新 更多