【问题标题】:Calling C code from within Ruby -- How to use the returned pointer?从 Ruby 中调用 C 代码——如何使用返回的指针?
【发布时间】:2013-01-05 05:09:42
【问题描述】:

问题:

我想在我的 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 中使用它?
  • 还有别的吗?

任何见解将不胜感激!

【问题讨论】:

    标签: c ruby windows winapi


    【解决方案1】:

    虽然我不知道为什么 Win32API 方法不起作用,但我找到了一个更简单的解决方案,使用 FFI 来解决从 Ruby 中调用 C 函数或与 DLL 交互的问题。


    使用FFI的解决方案

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

    • (1) 安装ffi (适用于 ruby​​ 1.9.3,ymmv 与以前的版本一起使用)

      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 的方法
    • @G_G:看看 String#inspect 是否有效。因此,如果您的字符串被称为 ret_str,请尝试将 ret_str.inspect 显示为 C 函数是否将字符串带入 Ruby。
    • 如果您使用 msbuild 之类的工具构建了自己的 dll,您能否将 FFI 指向 DLL 的完整路径,而不是在本示例中使用 user_32
    • @Schneems:应该可以。上面的示例使用了自定义 dll(不是 user32)。我在与 dll 相同的目录中运行程序,但您应该能够指向一个通用目录。请记住使用“\\”正确转义路径中的目录分隔符。
    猜你喜欢
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多