以Python ctypes为例,在Ruby中从FFI包装的C库中获取返回值时出现问题

2024-09-25 00:33:12 发布

您现在位置:Python中文网/ 问答频道 /正文

编辑:Documentation for the relevant library methods!

完全公开-我对C/C++几乎一无所知,对指针、缓冲区和所有有趣的东西也一无所知。如果这是一个愚蠢的问题,我深表歉意,但我已经为此工作了几个小时,根本没有走多远

所以我有一个外部C库,我从Ruby脚本中引用它。我需要访问一系列不同的函数,我有一些简单的函数可以使用,但我正在努力处理一个更复杂的函数

好的方面是,我有Python的工作示例代码

我面临的挑战是关于使用FFI的整个缓冲区方面——我还没有找到很多相关的例子来说明如何实现这一点,我有点挣扎

下面是我想用FFI在Ruby中复制的函数:

def getActiveID32():
    """
    Will return a tuple consisting number of bits
    read and actual data. Minimum 8 byte of data will
    be returned.
    """
    rawData = ""
    buffer_size = ctypes.c_short(32);
    pcproxlib.GetActiveID32.restype = ctypes.c_short
    # create a buffer of given size to pass it
    # to get the raw data.
    raw_data_tmp = (ctypes.c_ubyte * buffer_size.value)()
    #as per documentation 250 millisecond sleep is required
    # to get the raw data.
    time.sleep(250/1000.0)
    nbBits = pcproxlib.GetActiveID32(raw_data_tmp , buffer_size)
    bytes_to_read = int((nbBits + 7) / 8) ;
    # will read at least 8 bytes
    if bytes_to_read < 8:
        bytes_to_read = 8

    for i in range(0 , bytes_to_read):
        temp_buf = "%02X " % raw_data_tmp[i]
        rawData = temp_buf + rawData
    return (nbBits , rawData)

下面是我为简单函数(例如BeepNow)编写的示例代码,但不是为这个更复杂的函数getActiveID32编写的

其中包含了我一直在使用的代码,但这显然过于简化,不起作用

require 'ffi'

module PCProxLib
  extend FFI::Library
  ffi_lib File.expand_path('lib/32/libhidapi-hidraw.so.0')
  ffi_lib File.expand_path('lib/32/libpcProxAPI.so')
  attach_function :usbConnect, [], :short
  attach_function :getPartNumberString, [], :string
  attach_function :getActiveID32, [:string, :int], :int
  attach_function :getActiveID, [], :int
  attach_function :BeepNow, [:int, :bool], :bool
end

puts PCProxLib.usbConnect()

puts PCProxLib.BeepNow(3, false)

sleep 0.25
buffer = ""
puts PCProxLib.getActiveID32(buffer, 64)
puts buffer

感谢您的帮助:)

编辑:

根据@matt的评论,我将代码修改为:

require 'ffi'

module PCProxLib
  extend FFI::Library
  ffi_lib File.expand_path('lib/32/libhidapi-hidraw.so.0')
  ffi_lib File.expand_path('lib/32/libpcProxAPI.so')
  attach_function :usbConnect, [], :short
  attach_function :USBDisconnect, [], :short
  attach_function :getActiveID32, [:pointer, :int], :int
end

PCProxLib.usbConnect()

puts PCProxLib.getPartNumberString() // verifying connection to reader

def read_card
  sleep 0.5
  buffer = FFI::MemoryPointer.new(:uint8, 32)
  bits_written = PCProxLib.getActiveID32(buffer, 32)
  puts bits_written
  puts buffer.read_bytes(32)
end

20.times { read_card }

PCProxLib.USBDisconnect()

当我扫描一张卡(代码循环时,bits_written值从0跳到32,这看起来不错。但是buffer.read_bytes(32)值始终为空

我尝试过其他一些MemoryPointer方法(如readread_string等),但得到了相同的结果

如果我尝试.inspect使用buffer.read_bytes(32),我会得到:

"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

…哪个有趣?这是一样的,无论我是否扫描一张卡


Tags: to函数readdatabyteslibbufferfunction
1条回答
网友
1楼 · 发布于 2024-09-25 00:33:12

函数getActiveID32的签名看起来像

int getActiveID32(uint8_t *buffer, int size)

您可以使用它,首先创建一个要写入的缓冲区,然后在调用它时将指向该缓冲区的指针及其大小作为参数传递,类似于:

// Define BUFFER_SIZE first.
uint8_t *buffer = malloc(BUFFER_SIZE);
int bits_written = getActiveID32(buffer, BUFFER_SIZE);
// There are now bits_written bits in buffer for you to use.
// You will need to call free on buffer when you are finished.

使用Ruby的FFI,您可以使用Pointer实现这一点。首先声明函数以接受指针(字符串类似于指针,但在本例中,我们需要直接使用指针):

attach_function :getActiveID32, [:pointer, :int], :int

现在,当您想要调用它时,首先需要创建一个所需大小的MemoryPointer,并将其作为参数传递给函数。然后,FFI将在调用C函数时将其转换为指向已分配内存的指针

# Assuming you want a 32 byte buffer.
buffer = FFI::MemoryPointer.new(:uint8, 32)
bits_written = PCProxLib.getActiveID32(buffer, 32)

现在buffer将包含函数写入的数据。您可以使用methods on ^{}访问该数据,例如,要将该数据复制到可以使用buffer.read_bytes(32)的Ruby(二进制)字符串中

MemoryPointer还将处理在垃圾收集时释放分配的内存

相关问题 更多 >