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的调用,请参考:
这里不在赘述。但是这些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是合法的,不能越界,否则可能出现非法指针访问。