从Ruby中调用C代码--如何使用返回的指针?

7
问题:

我想在我的Ruby程序中使用一个编写在C语言中的算法,并通过DLL公开。

我想将该算法视为一个黑盒,可以从Ruby中调用——只需传入所需参数并使用结果即可。

Ruby(包括1.8.7和1.9.3版本)具有Win32API模块,似乎旨在使接口动态库变得非常容易,以实现我要求的功能。

但是问题是,我似乎无法让Win32API调用返回一个字符串。

细节:

第三方C函数是CodeGen()。它需要6个参数,包括源字符串、任意字符串作为加密密钥,以及为简单起见的4个数值参数,其中包括一个带符号整数、一个无符号长整型和两个无符号短整型。从这些参数中,CodeGen()实现了一个黑盒算法来返回一个结果字符串。

CodeGen()的C原型为:

const char *CodeGen(  int encryp_level, 
                     const char *source_str, const char *encryp_key,
                     unsigned long param_a, 
                     unsigned short param_b, unsigned short param_c
                  )

请注意,这两个输入字符串都是常量,即它们作为字符串提供给CodeGen() -- 所以指向常量字符串的指针。
CodeGen()的返回值也是一个字符串,具有固定的最大长度,因此它将返回一个指针。
我的问题:
如何设置对CodeGen()的调用并获取它应该生成的字符串?
我的尝试:
下面的代码仅以整数形式返回值,而我希望获得一个字符串。
require 'Win32API'

codeGen = Win32API.new("encrypt.dll", "CodeGen", "ISSIII", "S")

ret_str = codeGen.Call(3, "foo", "bar", 0, 0, 0)

puts ret_str

然而,与其得到字符串,我得到了一个整数。编辑:这可能是一个指针吗? 虽然我使用的是Windows 7的64位版本上的Ruby 1.9.3,但我还在Windows XP的32位版本上测试过上述内容,并使用了Ruby 1.8.7,所以我很确定问题与Win32API本身有关。
我不确定问题是否出在以下任何一点:
- 这些整数(3、0等)需要被打包吗? - 我需要区分短类型和长类型吗? - 我没有正确处理返回值吗? - 如果返回值是一个指针,我该如何在Ruby中使用它? - 还有其他什么问题吗?
非常感谢您的任何见解!
1个回答

4
尽管我不知道为什么使用Win32API方法无法解决问题,但我找到了一种更简单的解决方案,即使用FFI来调用C函数或与DLL进行交互。

使用FFI的解决方案

在Ruby中使用FFI与DLL进行交互,步骤如下:

  • (1) 安装ffi模块 (适用于ruby 1.9.3,之前的版本可能有所不同)

    gem install ffi

  • (2) 创建一个自己的Ruby模块来包装C函数

require 'ffi'

module CodeGen                   # Ruby wrapper  (your choice)
  extend FFI::Library
  ffi_lib 'codegen'              # DLL name  (given)
  attach_function 
        :create_code,            # method name  (your choice)
        :CreateCodeShort3,       # DLL function name (given)
          [ :int, :string, :string, :uint, :ushort, :ushort], :string  
                          # specify C param / return value types 
end

ret_str = CodeGen.create_code(3, "foo", "bar", 0,0,0)

puts ret_str
  • (3) 运行。结果会给出所需的字符串。

完成!


我有类似的问题。我的返回char*指针实际上指向UTF-16文本(它不像你的示例中是ASCII文本)。我找不到将这样的字符串带入Ruby的方法。 - Gianluca Ghettini
@G_G:看看String#inspect是否起作用。所以,如果你的字符串叫做ret_str,请尝试puts ret_str.inspect。这应该会显示C函数是否将字符串带入Ruby。 - Assad Ebrahim
如果您使用类似于msbuild的工具构建了自己的dll,那么您是否可以直接将FFI指向DLL的完整路径,而不是像这个例子中我们使用"user_32"一样? - Schneems
@Schneems:应该可以工作。上面的示例使用了自定义dll(而不是user32)。我在与dll相同的目录中运行程序,但您应该能够指向通用目录。只需记住使用“\”正确转义路径中的目录分隔符即可。 - Assad Ebrahim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接