首页  编辑  

Ruby中调用DLL动态库对返回值中指针结构数据的处理

Tags: /Ruby/   Date Created:
How to process return pointer structure data for DLL/windows api in Ruby?
Copyright Kingron, 2011-10-29。版权所有,谢绝转载。

在Ruby中,能够很容易调用DLL,利用Win32API/dl 就可以了,或者win32-api等等都可以,对于普通的API的调用,请参考:
http://developer.51cto.com/art/200912/169459.htm
http://stackoverflow.com/questions/1025086/how-do-i-call-windows-dll-functions-from-ruby
http://www.rubytips.org/2008/05/13/accessing-windows-api-from-ruby-using-win32api-library/

这里不在赘述。但是这些gem或者方式最大的一个问题是,如果你的DLL返回值是一个指向某个结构体的指针,那么你就无法正确处理了。网络上有人提到过这个问题,http://www.iteye.com/topic/47936 ,但找遍网络也没有解决答案。因为Win32API只能够支持返回指针,而Ruby对返回的export数据处理的有问题,他不能指定export数据大小,只能是简单的'P'类型,因此只能复制4字节数据到返回的结果中。由于对Ruby的gems了解不深,可能有其他的win32 api 相关的gems可以解决这个问题。
For example:

If the header file of C/C++ define a pointer and struct like below:
typedef  struct Data {
    int len;
    int flag;
    char values[1000];
  } *Data;
  PData = *Data;

If you have a dll, which export a function:
  GetData(int index, int flag): PData;

我们可以在Ruby中定义:

require "Win32API"
getData = Win32API.new('filename.dll', 'GetData', ['I', 'I'], 'P')

然后我们可以调用:

pd = getData.call(100, 0)

然而, pd 返回的数据只有几个字节!如果 pd 返回的是空字符结束的字符串,在Ruby中按上面的方式也是没有问题的。然而,如果你是一个复杂的指向结构的指针,那就不行了。比如,上面的就不行。

要在Ruby中处理上面的返回数据,有一种解决方法是用复制内存块的方式来实现。
首先定义CopyMemory函数,这是Windows SDK的:

    @CopyMemory = Win32API.new('Kernel32.dll', 'RtlMoveMemory', ['P', 'I', 'I'], '0')

函数定义与前面类似,但是export必须定义成"L",所以即:

getData = Win32API.new('filename.dll', 'GetData', ['I', 'I'], 'L')

然后调用:

pd = getData.call(100, 0)

此时的 pd,是一个指针,我们通过这个指针用@CopyMemory复制数据到Ruby的String即可:

buf = Array.new(buf_size, 0).pack('L*')
@CopyMemory.call(buf, pd, buf_size)

此时buf就是返回的结构化的数据了,buf_size就是struct的大小,当然也可以是动态的数据,但你必须保证buf_size是合法的,不能越界,否则可能出现非法指针访问。