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

问题描述 投票:2回答:1

Introduction

我正在开发一个代码生成器,它将生成围绕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类型之间没有转换代码。这就是我的问题所在。

Getting close to the problem

ctypes文档说如果使用“基本数据类型”指定加载的DLL函数的argtypes属性,那么当调用加载的DLL函数时,ctypes将为您转换为python类型。这很好,因为在这种情况下,我生成的代码看起来像上面的例子 - 我不需要显式地将ctypes对象转换为返回值的python类型值,反之亦然。

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

这是the ctypes docs关于它的摘录:

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

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

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

似乎我需要知道类型是“基础”还是“子类”。这将帮助我定义代码的生成方式,即对于“基本”类型,生成的代码看起来与上面的示例类似,对于“基本”类型的子类,它将从ctypes对象转换为合理的python类型。 (或者生成器只会抛出一个异常,说“这不受支持”)。

Question:

How can I distinguish between "fundamental ctypes data types" and "subclasses of fundamental ctypes data types"?

我查看了ctypes python模块的代码,发现c_void_pc_char_p都是ctypes._SimpleCData的子类,因此一个不以任何方式成为另一个的子类。

另外,我是否误解了这个问题也适用于输入参数,或者这只是返回值的交易?

python parameter-passing ctypes dllimport
1个回答
1
投票

...我是否误解了这个问题也适用于输入参数,或者这只是返回值的交易?

它不适用于输入参数,如下面的序列所示:

>>> 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

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

>>> dll.ctime.argtypes = c_void_p,
>>> dll.ctime.restype = c_char_p
>>> dll.ctime(byref(c_int(5)))
b'Wed Dec 31 16:00:05 1969\n'
>>> dll.ctime.restype = LPCSTR
>>> dll.ctime(byref(c_int(5))) # not converted to Python byte string
LPCSTR(1989707373328)
>>> x = dll.ctime(byref(c_int(5))) # but can get the value
>>> x.value
b'Wed Dec 31 16:00:05 1969\n'
© www.soinside.com 2019 - 2024. All rights reserved.