在面向对象的Python中使用ctypes调用DLL文件中的函数时遇到问题
我希望这个问题不是已经有人回答过的,也不是个愚蠢的问题。最近我在用几种工具编程,想让它们之间进行通信,以便创建一个测试程序。不过,我在使用某个特定工具时遇到了一些问题,尤其是在调用我从工具的DLL文件中“屏蔽”掉的函数时。
当我在交互式的Python环境中使用这些函数时,一切都运行得很好(虽然有点繁琐)。但是当我用面向对象的方式来实现这些函数时,程序就出问题了。其实并不是崩溃,而是没有任何反应。以下是我调用的第一个方法:(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(一个包含三个数字的数组), fmin, fmax, fcrossover, error_string(超过80个字符), 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 个回答
在add-controller或add-tuner中,有哪些参数实际上是返回值吗?
我强烈建议你定义函数的原型,而不是直接用所有参数的类型转换来调用它们。
我相信你已经看过这个页面了,但你需要关注的部分是函数原型。这会让代码看起来更整洁,也更容易追踪和调试。
另外,正如Mark Rushakoff提到的,使用pointer(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)
我不太确定你具体遇到的问题是什么,但这里有几个一般性的建议:
对于那些在构造函数外部调用的函数,我强烈建议你在构造函数中设置它们的 argtypes
。一旦你声明了 argtypes
,就不需要再把所有参数转换成 c_short
、c_double
等类型了。此外,如果你不小心把错误的参数传给 C 函数,Python 会抛出运行时错误,而不是让 DLL 崩溃。
还有一个小细节,你应该使用 x = 0;
byref(x)
,或者可能用 POINTER
(c_double)()
,而不是在调谐器和控制器中使用 pointer(c_double())。
我最近也在用 Python 2.6 写一些 ctypes 类,没遇到你描述的问题。而且似乎也没有相关的 Python bug 报告,所以我相信我们可能在你遇到问题的方法中忽略了一个小细节。
我发现调用导出的DLL中的函数的唯一方法是通过在每个方法中使用DLL作为参数。(这个程序导出DLL,在每个被调用的方法中都会把它作为参数传递)。这看起来挺难看的,但这是我找到的唯一有效的方法。我甚至尝试把DLL作为类的属性来导出。由于我正在处理的系统比较复杂,所以我猜里面可能有一些糟糕的代码导致它失败。感谢大家的反馈和建议!
/Mazdak