使用ctypes获取返回参数的正确方法

时间:2014-10-13 04:49:44

标签: python pointers ctypes

我熟悉python但不熟悉ctypes。

我一直在玩这个特定的功能(VCS_GetErrorInfo),但无法让它工作。我的问题是访问返回参数。我正在使用Windows 64 Bit,但在Linux下也尝试过。完整的文档,在这种情况下,可以找到Windows库here

我想打电话的功能就像那样。

BOOL VCS_GetErrorInfo(DWORD ErrorCodeValue, char *pErrorInfo, WORD MaxStrSize)
  • 参数
    • ErrorCodeValue,DWORD,收到错误代码
    • MaxStrSize,WORD,Max。错误字符串的长度
  • 返回参数
    • pErrorInfo,char *,错误字符串
    • 返回值:BOOL,如果发现错误信息,则为非零;否则为0

在pErrorInfo中,我期待"没有通信错误"。

我的代码(已编辑) ...

import ctypes
from ctypes import wintypes

lib = wintypes.WinDLL(r"...\EposCmd64.dll")

lib.VCS_GetErrorInfo.restype = wintypes.BOOL
error_buf_size = ctypes.create_string_buffer(10) # creates a buffer
pErrorInfo = ctypes.c_char_p(ctypes.addressof(error_buf_size)) # creates a pErrorInfo from the error buffer
# And when passing in the function make sure to wrap in the ctypes.byref
result = lib.VCS_GetErrorInfo(wintypes.DWORD(0), ctypes.byref(pErrorInfo), wintypes.WORD(10))
print result
# Later to get the value of the error string
print pErrorInfo, pErrorInfo.value

输出

1
c_char_p('')

仍然没有输出。

错误文档

0x0000 0000, No Communication Error
0x0503 0000, Toggle Error
0x0504 0000, SDO Time Out
...

如果我尝试使用其他错误代码。

result = lib.VCS_GetErrorInfo(wintypes.DWORD(0x05030000), ctypes.byref(pErrorInfo), wintypes.WORD(10))
print result
0
c_char_p('')

我可能错误地传递了ErrorCodeValue。

我也尝试过使用Ubuntu的方法:

import ctypes

ctypes.cdll.LoadLibrary("/path/to/libftd2xx.so")
lib = ctypes.cdll.LoadLibrary("/path/to/libEposCmd.so.5.0.0.3")

def VCS_GetErrorInfo(ErrorCodeValue, MaxStrSize):
    lib.VCS_GetErrorInfo.restype = ctypes.c_bool
    error_buf_size = ctypes.create_string_buffer(MaxStrSize)
    pErrorInfo = ctypes.c_char_p(ctypes.addressof(error_buf_size))
    result = lib.VCS_GetErrorInfo(ctypes.c_uint32(ErrorCodeValue), ctypes.byref(pErrorInfo), ctypes.c_uint16(MaxStrSize))
    if not result:
        raise RuntimeError('get error info failed = {}'.format(pErrorInfo.value))
    return pErrorInfo.value

结果看起来很奇怪。

print repr(VCS_GetErrorInfo(0, 10))
'\x05'
print repr(VCS_GetErrorInfo(0, 20))
'0\xe7$\xd7j\x7f'
print repr(VCS_GetErrorInfo(0x05040000, 20))
RuntimeError: get error info failed =
print repr(VCS_GetErrorInfo(0x05040000, 30))
Segmentation fault (core dumped)

2 个答案:

答案 0 :(得分:2)

使用__cdecl调用约定而不是__stdcall的DLL,因此使用CDLL而不是WinDLL

>>> from ctypes import *
>>> lib=CDLL('EposCmd64')
>>> buf=create_string_buffer(80)
>>> lib.VCS_GetErrorInfo(0x05030000,buf,80)
1
>>> buf.value
b'Toggle Error'

请注意,例如,所有额外的对象构造wintypes.DWORD(0x05030000)都不是必需的。如果它是一个整数或一个指针,ctypes会弄清楚它,但需要告诉它结构和浮点值。在这种情况下不需要它,但这是如何显式定义参数和返回值:

>>> from ctypes import *
>>> from ctypes import wintypes
>>> lib=CDLL('EposCmd64')
>>> lib.VCS_GetErrorInfo.argtypes=[wintypes.DWORD,c_char_p,wintypes.DWORD]
>>> lib.VCS_GetErrorInfo.restype=wintypes.BOOL
>>> buf=create_string_buffer(80)
>>> lib.VCS_GetErrorInfo(0x05040000,buf,80)
1
>>> buf.value
b'SDO Protocol Timeout'

由于额外的类型检查和参数匹配,它还可以防止错误地调用函数:

>>> lib.VCS_GetErrorInfo(0x05040000,buf)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: this function takes at least 3 arguments (2 given)

0返回空字符串(无错误):

>>> lib.VCS_GetErrorInfo(0,buf,80)
1
>>> buf.value
b''

答案 1 :(得分:0)

通常我使用类似的东西来创建错误消息缓冲区:

error_buf_size = ctypes.create_string_buffer(10) # creates a buffer
pErrorInfo = ctypes.c_char_p(ctypes.addressof(error_buf_size)) # creates a pErrorInfo from the error buffer

# And when passing in the function make sure to wrap in the ctypes.byref
result = lib.VCS_GetErrorInfo(wintypes.DWORD(0), ctypes.byref(pErrorInfo), wintypes.WORD(10))

# Later to get the value of the error string
if pErrorInfo.value:
    print (pErrorInfo.value)

检查ctypes doc byrefaddressof