区分“基本” ctypes数据类型及其子类?

时间:2019-03-07 18:49:02

标签: python parameter-passing ctypes dllimport

简介

我正在开发一个代码生成器,该代码生成器将围绕ctypes.cdll个加载函数生成函数。生成器将获取有关参数ctypes和返回值的信息,并生成如下行为(在某种程度上看起来像):

func = getattr(dll, 'UglyLongAndUselessCName')
func.argtypes = [ctypes.c_uint32, ctypes.c_int8, ctypes.c_char_p]
func.restype = ctypes.c_int16

def nice_python_name(handle: int, arg1: int, arg2: str) -> int:
    return func(handle, arg1, arg2)

请注意python类型注释如何与函数参数的ctypes数据类型很好地配合使用。另请注意,nice_python_name函数和func函数中的python类型之间没有转换代码。这就是我的问题。

解决问题

ctypes文档说,如果使用“基本数据类型”指定了已加载DLL函数的argtypes属性,则在调用已加载的DLL函数时,ctypes将执行为您转换为python类型。很好,因为在这种情况下,我生成的代码将类似于上面的示例-我不需要将ctypes对象显式转换为返回值的python类型的值,而将参数转换为相反的值。

但是,文档还说,对于“基本数据类型的子类”,此技巧不起作用,并且调用已加载的DLL函数将需要ctypes个对象作为参数,结果将是{{ 1}}对象。

这是the ctypes docs的摘录:

  

基本数据类型在作为外部函数调用结果返回时,或者例如通过检索结构字段成员或数组项而返回时,将透明地转换为本机Python类型。换句话说,如果外部函数的ctypesrestype,则将始终收到Python字节对象,而不是c_char_p实例。

     

基本数据类型的子类不继承此行为。因此,如果外部函数c_char_prestype的子类,您将从函数调用中收到此子类的实例。当然,您可以通过访问value属性来获取指针的值。

所以,我想解决这个问题。

似乎我需要知道类型是“基本”还是“子类”。这将帮助我定义代码的生成方式,即,对于“基本”类型,所生成的代码将类似于上述示例,对于“基本”子类型,它将具有c_void_p对象的额外转换转换成合理的python类型(否则生成器将抛出一个异常,提示“不支持此功能”)。

问题:

如何区分“基本ctypes数据类型”和“基本ctypes数据类型的子类”?

我查看了ctypes python模块的代码,发现ctypesc_void_p都是c_char_p的子类,因此一个不是另一个中的子类。任何方式。

还,我是否会误解为这个问题也适用于输入参数,或者这仅仅是对返回值的处理?

1 个答案:

答案 0 :(得分:1)

  

...我是否会误解为这个问题也适用于输入参数,还是仅适用于返回值?

它不适用于输入参数,如以下顺序所示:

p?????ple

转换对于输入子类型仍然有效;但是,输出子类型的工作方式与您提到的文档引用不同:

>>> dll=CDLL('msvcrt')
>>> dll.printf.argtypes = c_char_p,
>>> dll.printf(b'abc') # Note: 3 is the return value of printf
abc3
>>> class LPCSTR(c_char_p): # define a subtype
...  pass
...
>>> dll.printf.argtypes = LPCSTR,
>>> dll.printf(b'abc')
abc3