【问题标题】:Can I use pybind to load an external c++ dll and call functions?我可以使用 pybind 加载外部 c++ dll 并调用函数吗?
【发布时间】:2020-01-29 13:27:05
【问题描述】:

我正在尝试与仅提供 dll(c++) 的 PoS 终端进行交互。我可以使用pybind加载dll并通过一些代码调用api函数吗:

import pybind11

mydll = pybind.load("/path/to/dll")

mydll.api_call(param1,param2)

我在 ctypes 中尝试了以下代码。但无法让 API 正常工作或出现清晰的错误。

from ctypes import *
import ctypes.wintypes as wintypes
mydll = windll.LoadLibrary("C:\madaapi_v1_7.dll")
mydll.api_GetCOMTerminalID.argtypes = POINTER(c_char_p), POINTER(wintypes.DWORD), POINTER(c_byte), POINTER(c_byte),POINTER(c_byte),POINTER(c_char_p),POINTER(c_int),POINTER(c_byte)


bPort,dwBaudRate,bParity,bDataBits,bStopBits, inReqBuff, inReqlen, terminal_id = c_char_p(b'COM3'),wintypes.DWORD(38400),c_byte(0),c_byte(8),c_byte(1),c_char_p(b'07!'),c_int(3),c_byte()

conn = mydll.api_GetCOMTerminalID(byref(bPort),byref(dwBaudRate),byref(bParity),byref(bDataBits),byref(bStopBits),byref(inReqBuff),byref(inReqlen),byref(terminal_id))
print(conn)

这将返回带有或不带有参数的代码 -3。根据文档 -3 是指“无法打开端口”。但我可以看到端口并使用 PySerial 打开它。

根据 dll 文档,这些是输入数据类型:

int api_GetCOMTerminalID (BYTE bPort, DWORD dwBaudRate, BYTE bParity, BYTE bDataBits, BYTE bStopBits, unsigned char* inReqBuff, int *inReqLen, BYTE *terminalId)

这是一个示例调用:

api_GetCOMTerminalID (3,38400,0,8,0,”07!”,3)

【问题讨论】:

  • 不是这样,因为 pybind11 没有运行时组件(这是它的主要卖点)。但是您要问的正是 ctypes 提供的功能。您不想使用 ctypes 的原因是该函数具有 C++(而不是 C)签名吗?
  • @WimLavrijsen 我已经用我在 ctypes 中尝试过的代码编辑了这个问题。但它没有用。
  • 函数的签名是什么?如果所有参数都需要 byref(尤其是端口的命名),我会感到惊讶。
  • 我用函数所需的数据类型再次编辑了我的问题。我不知道如何在调用中声明、分配和传递它。我尝试了很多方法,只得到错误和错误。
  • 是的,所以那些byrefs 不应该出现在大多数争论中。在上面的mydll.api_GetCOMTerminalID 中,删除除inReqlenterminal_id 上的所有byrefs。由于 byref 传递的是一个指针,因此在其他情况下您传递的是或多或少的随机值,而不是预期的值。

标签: python-3.x pybind11


【解决方案1】:

我无权访问您正在使用的代码,但假设您发布的此签名是正确的:

int api_GetCOMTerminalID (BYTE bPort, DWORD dwBaudRate, BYTE bParity, BYTE bDataBits, BYTE bStopBits, unsigned char* inReqBuff, int *inReqLen, BYTE *terminalId)

然后下面的 ctypes 代码将正确地获取所有类型:

from ctypes import *
import ctypes.wintypes as wintypes

BYTE      = wintypes.BYTE
DWORD     = wintypes.DWORD
c_uchar_p = POINTER(c_ubyte)
c_int_p   = POINTER(c_int)
c_BYTE_p  = POINTER(BYTE)

mydll = windll.LoadLibrary("C:\madaapi_v1_7.dll")
mydll.api_GetCOMTerminalID.argtypes = \
    (BYTE,          # BYTE bPort
     DWORD,         # DWORD dwBaudRate
     BYTE,          # BYTE bParity
     BYTE,          # BYTE bDataBits
     BYTE,          # BYTE bStopBits
     c_uchar_p,     # unsigned char* inReqBuff
     c_int_p,       # int *inReqLen
     c_BYTE_p,      # BYTE *terminalId
mydll.api_GetCOMTerminalID.restype = c_int

以及示例用法:

bPort, dwBaudRate, bParity, bDataBits, bStopBits = \
    BYTE(3), DWORD(38400), BYTE(0), BYTE(8), BYTE(0)
inReq        = b'07!'
inReqBuff    = (c_ubyte * len(inReq))()
for i in range(len(inReq)):
    inReqBuff[i] = int(inReq[i])
inReqLen     = c_int(3)
terminal_id  = BYTE(0)

conn = mydll.api_GetCOMTerminalID(bPort, dwBaudRate, bParity, bDataBits, bStopBits,
                                  inReqBuff, byref(inReqLen), byref(terminal_id))

【讨论】:

  • 另外,你能告诉我API返回类型是否是char数组缓冲区,在func.argstype中要声明什么数据类型?
  • 对于返回类型,您会设置func.restype,而不是func.argtypes,所以没有真正关注?如果返回类型,则 POINTER(c_ubyte) 将起作用。还是您的意思是一个字符数组输出参数(即char**unsigned char**)?
  • 我把问题框定错了。感谢您的帮助。我了解如何在通话中使用正确的类型。自过去 10 天以来,我一直在寻求帮助。您的解决方案有效。还有一件事,在 API 调用之后 terminal_id 是 c_byte(48) ,为了转换为人类可读,我使用了 terminal_id.value 并打印了 48。我想知道它是否是真正的 terminal_id
  • 我无法直接验证,但是是的,这就是终端 id 应该返回的方式,所以我希望它是正确的。我没有更明确的另一个原因是BYTEunsigned char 的typedef,48 是ASCII 中的'0'。零可以表示“第一个”(预期),也可以表示“错误”(即失败)。检查文档,如果没有任何说明,我会打开另一个,看看你是否收到49。除此之外,正如我所说:我无法直接验证。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-22
  • 2014-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
相关资源
最近更新 更多