在面向对象的 Python 中使用 ctypes 从 DLL 文件运行函数时出现问题
-
19-09-2019 - |
题
我当然希望这不会是一个已经回答的问题或一个愚蠢的问题。最近我一直在使用几种乐器进行编程。尝试在它们之间进行通信以创建测试程序。然而,当我尝试调用从仪器 DLL 文件中“屏蔽”出来的函数时,我在某一特定仪器上遇到了一些问题。当我使用交互式 python shell 时,它工作得很好(尽管它有很多单词破坏)。但是,当我以面向对象的方式实现这些功能时,程序失败了,实际上它并没有失败,只是什么也没做。这是第一个被调用的方法:(ctypes 和 ctypes.util 已导入)
def init_hardware(self):
""" Inits the instrument """
self.write_log("Initialising the automatic tuner")
version_string = create_string_buffer(80)
self.error_string = create_string_buffer(80)
self.name = "Maury MT982EU"
self.write_log("Tuner DLL path: %s", find_library('MLibTuners'))
self.maury = WinDLL('MlibTuners')
self.maury.get_tuner_driver_version(version_string)
if (version_string.value == ""):
self.write_log("IMPORTANT: Error obtaining the driver version")
else:
self.write_log("Version number of the DLL: %s" % version_string.value)
self.ThreeTypeLong = c_long * 3
现在效果很好,一切都很完美,我得到了完美的日志条目。但是当我尝试在程序中进一步运行一个方法时,调用:
def add_tuner_and_controller(self, name, serial_number, tuner_number=0):
""" Adds the tuner to the driver object, controller is inside the tuner """
self.write_log("Adding tuner %d and the built-in controller" % tuner_number)
TempType = self.ThreeTypeLong()
self.maury.add_controller(c_short(tuner_number), c_char_p(self.file_path), c_char_p(name), c_int(0), c_int(0),
c_long(0), c_short(serial_number), self.error_string)
self.maury.add_tuner(c_short(tuner_number), c_char_p(name), c_short(serial_number), c_short(0),
c_short(1), pointer(c_double()), TempType, pointer(c_double()), pointer(c_double()),
pointer(c_double()), self.error_string)
程序突然停止工作/继续运行,当调用“self.maury”行时没有任何反应。当我将所有内容放入 init_hardware 方法中时,它工作得很好,所以我猜测存在轻微的内存“错误”或具有面向目标的结构的内容。我真的希望它保持这种方式,有没有办法以这种方式隔离功能?或者我是否必须将自己限制在一大块代码中?
编辑:
文档信息:
[传奇:星号表示指针,括号表示数组]
add_tuner 函数在调谐器驱动程序对象中添加或更新一个调谐器。
short add_tuner(short tuner_number, char model[ ], short serial_number, short ctlr_num, short ctlr_port, short *no_of_motors, long max_range[ ], double *fmin, double *fmax, double *fcrossover, char error_string[ ])
输出: no_motors, max_range (array of three numbers), fmin, fmax, fcrossover,error_string (80+ characters long), function-return->Error flag
add_controller 函数在调谐器驱动程序对象中添加或更新一个控制器
short add_controller(short controller_number, char driver[ ], char model[ ], int timeout, int address, long delay_ms, char error_string[ ])
输出: error_string, function-return->Error flag
解决方案 3
我发现调用导出的 DLL 中的函数的唯一方法是通过每个方法中的参数使用 DLL。(程序导出 dll,并在调用它的每个方法中将其作为参数发送)。它看起来很丑陋,但这是我发现为我工作的唯一方法。我什至尝试将 DLL 作为类属性导出。我正在使用的系统非常庞大,所以我猜想某个地方有一些 boboo 代码导致它失败。感谢您的所有反馈和提示!
/马自达克
其他提示
我不确定您的确切问题,但这里有一些一般提示:
对于那些在构造函数之外调用的函数,我强烈建议设置它们 argtypes
在构造函数中也是如此。一旦你宣布了 argtypes
, ,您不需要将所有参数转换为 c_short
, c_double
, , ETC。此外,如果您不小心将不正确的参数传递给 C 函数,Python 将引发运行时错误,而不是在 DLL 中崩溃。
另一个小细节,但你应该使用 x = 0;
byref(x)
或者可能 POINTER
(c_double)()
而不是调谐器和控制器中的指针(c_double())。
我最近也在Python 2.6中编写了一些ctypes类,但我没有看到任何像你所描述的问题。由于显然也没有任何关于此的 Python 错误报告,因此我坚信,在您的方法中,我们都忽略了一个有问题的微小细节。
add-controller 或 add-tuner 中的任何参数是否实际返回值?
强烈建议您定义函数的原型,而不是通过所有参数的强制转换直接调用它们。
我确定您已经阅读过此页面, ,但您要查看的部分是函数原型。使代码更加清晰且更易于跟踪/调试。
另外,正如 Mark Rushakoff 也提到的那样,在调用中使用指针(c_double())之类的东西非常令人讨厌。我对 POINTER() 的运气要好得多,并再次建议您将值预先声明为变量,并在函数调用中传递该变量。那么至少你可以在事后检查它的值是否有奇怪的行为。
编辑:所以你的原型和调用将如下所示:
prototype = WINFUNCTYPE(
c_int, # Return value (correct? Guess)
c_short, # tuner_number
c_char_p, # file_path
c_char_p, # name
c_int, # 0?
c_int, # 0?
c_long, # 0?
c_short, # serial_number
c_char_p, # error_string
)
# 1 input, 2 output, 4 input default to zero (I think; check doc page)
paramflags = (1, 'TunerNumber' ), (1, 'FilePath' ), (1, 'Name' ), ...
AddController = prototype(('add_controller', WinDLL.MlibTuners), paramflags)
那么你的调用就干净多了:
arg1 = 0
arg2 = 0
arg3 = 0
AddController(tuner_number, self.file_path, name, arg1, arg2, arg3,
serial_number, self.error_string)