【发布时间】:2022-06-17 03:18:31
【问题描述】:
我在使用 ctypes 连接高斯计 (F.W. Bell 5180) 时遇到问题。制造商提供接口的 DLL 和一个名为 FWB5180.h 的头文件:
FWB5180.h:
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the USB5100_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// USB5100_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef USB5100_EXPORTS
#define USB5100_API __declspec(dllexport)
#else
#define USB5100_API __declspec(dllimport)
#endif
extern "C" USB5100_API unsigned int openUSB5100(void);
extern "C" USB5100_API void closeUSB5100(unsigned int fwb5000ID);
extern "C" USB5100_API int scpiCommand(unsigned int usbID, char* cmd, char* result, int len);
我的 ctypes Python 代码如下,我要做的就是初始化设备然后关闭它:
import ctypes,time
# open DLL
path = "C:\\Users\\Roger\\fw_bell_magnetic_field_probe\\usb5100-x64\\x64-dist\\usb5100.dll"
fwbell = ctypes.WinDLL(path)
# define open and close functions with argument and return types
openUSB5100 = fwbell.openUSB5100
openUSB5100.argtypes = None
openUSB5100.restype = ctypes.c_int
closeUSB5100 = fwbell.closeUSB5100
closeUSB5100.argtypes = [ctypes.c_int]
closeUSB5100.restype = None
# open device
idn = openUSB5100()
print(idn, type(idn))
# close device
time.sleep(0.1)
closeUSB5100(idn)
预期的行为:它在文档的其他地方说idn 是一个四位无符号整数,所以它应该返回一个像 10203045 这样的数字。所以我希望idn 像这样,并且在关闭连接时不会出错.
实际行为:在我的代码中,openUSB5100 始终返回 0,无论设备是否插入。 print 语句始终输出 0 <class 'int'>。然后,closeUSB5100 函数会出现类似OSError: exception: access violation reading 0x0000000000000028 的错误。我也尝试过为idn 使用不同的类型,比如c_ulong,但这似乎没有帮助。
我在使用 python 3.9 的 Windows 10 上。两者都是 64 位的,DLL 也是。
我会注意到,我可以使用他们提供的虚拟程序来使用高斯计,因此严格来说这不是连接问题。不过,我认为他们正在使用带有 32 位驱动程序的 32 位应用程序,因为当我尝试使用他们似乎使用的 DLL 时,我收到以下错误:OSError: [WinError 193] %1 is not a valid Win32 application。当我使用标记为 64 位的 DLL 时,我没有收到此错误。
对我的代码有什么建议或尝试吗?这是我第一次涉足 ctypes,所以我接受可能存在严重错误。
编辑: 这是我收到的确切错误消息。
PS C:\Users\Roger\fw_bell_magnetic_field_probe\usb5100-x64\x64-dist> python .\fw_bell_py.py
Traceback (most recent call last):
File "C:\Users\Roger\fw_bell_magnetic_field_probe\usb5100-x64\x64-dist\fw_bell_py.py", line 30, in <module>
idn = openUSB5100()
OSError: exception: access violation reading 0x00000000B56B1D68
如果我多次运行代码,最后一个值 0x ... 通常会发生一些变化。
此外,我发现高斯计在被 PC 检测到后,显然在准备使用时可能真的很慢。因此,如果我在插入设备(或使用 usb.core.list_devices 监视直到出现某些东西)和运行此代码之间等待很长时间,代码 always 在打开命令上出错,它不会进入关闭命令。
【问题讨论】:
-
从技术上讲,使用
openUSB5100.argtypes = ()表示无参数,使用ctypes.c_uint表示unsigned int参数,但在这种情况下应该没有区别。此外,WinDLL用于__stdcall函数,因此请使用CDLL,但这只会影响到 32 位的可移植性。WinDLL和CDLL在 64 位上是相同的调用约定。否则,我看不出代码有问题。关闭idn的错误是打开失败所以idn无效。 -
我做了您建议的更改,但不幸的是,他们没有解决问题。我确实发现在没有插入设备的情况下,我总是得到
idn=0,并且代码在关闭时失败。插入设备后,我第一次运行代码时会发生同样的事情,但在随后的代码运行中,我会在 open 语句中得到OSErrors。也许它与有两个 dll 相关,我在代码中明确引用的那个,usb5800.dll,取决于另一个 dll(本质上是 libusb-win32)。除了将其保存在工作目录中之外,我需要做些什么吗? -
我认为他们不会。对于这种简单的情况,您应该能够在不设置 argtypes 或 restype 的情况下调用
open,因此如果它在 C 中正常工作,这是某种路径问题。使用像 Process Monitor 这样的工具来监视 DLL 加载失败。见this answer。 -
OSError的回溯可能会提供线索,如果您想编辑您的问题并发布它。 -
哈哈,我的代码适用于 32 位库和 32 位 python,但是在 64 位 python 中运行的相同代码似乎不适用于 64 位库。 ProcMon 看起来也差不多。感谢您的帮助!
标签: python c++ python-3.x ctypes python-bindings