You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用Ruby Fiddle调用Win32 API时结构体被垃圾数据填充?

解决Ruby Fiddle调用Win32 API获取文件版本信息的结构体问题

我之前也折腾过类似的Win32 API调用,尤其是结构体这块,Fiddle对内存布局和类型匹配的要求真的很严格,你遇到的垃圾数据问题大概率是这里出了岔子,咱们一步步来捋清楚:

1. 先补全你提到的C代码逻辑

先把你没写完的C代码补全,方便对照着转Ruby:

DWORD dwHandle, dwLen;
LPVOID lpData;
UINT BufLen;
VS_FIXEDFILEINFO *pFileInfo;
LPTSTR LibName = L"C:\\windows\\notepad.exe";

// 获取版本信息所需内存大小
dwLen = GetFileVersionInfoSize(LibName, &dwHandle);
if (dwLen == 0) { /* 错误处理 */ }

// 分配内存存储版本数据
lpData = malloc(dwLen);
if (!lpData) { /* 错误处理 */ }

// 读取文件版本信息到内存
if (!GetFileVersionInfo(LibName, dwHandle, dwLen, lpData)) {
    free(lpData); /* 错误处理 */
}

// 提取VS_FIXEDFILEINFO结构体指针
if (!VerQueryValue(lpData, L"\\", (LPVOID*)&pFileInfo, &BufLen)) {
    free(lpData); /* 错误处理 */
}

// 读取版本信息示例
DWORD fileVerMS = pFileInfo->dwFileVersionMS;
DWORD fileVerLS = pFileInfo->dwFileVersionLS;
free(lpData);

2. Ruby Fiddle核心问题:结构体定义与指针传递

你遇到的垃圾数据,90%是结构体布局不匹配或者VerQueryValue的指针传递错误。先严格定义VS_FIXEDFILEINFO结构体:

require 'fiddle'
require 'fiddle/import'

module Win32VersionAPI
  extend Fiddle::Importer
  dlload 'version.dll'
  # 用宽字符版本的API(对应C里的L""字符串)
  extern 'DWORD GetFileVersionInfoSizeW(LPCWSTR, LPDWORD)'
  extern 'BOOL GetFileVersionInfoW(LPCWSTR, DWORD, DWORD, LPVOID)'
  extern 'BOOL VerQueryValueW(LPCVOID, LPCWSTR, LPVOID*, PUINT)'

  # 严格对应C的VS_FIXEDFILEINFO结构体成员顺序和类型
  class VS_FIXEDFILEINFO < Fiddle::Struct
    layout(
      :dwSignature, :dword,
      :dwStrucVersion, :dword,
      :dwFileVersionMS, :dword,
      :dwFileVersionLS, :dword,
      :dwProductVersionMS, :dword,
      :dwProductVersionLS, :dword,
      :dwFileFlagsMask, :dword,
      :dwFileFlags, :dword,
      :dwFileOS, :dword,
      :dwFileType, :dword,
      :dwFileSubtype, :dword,
      :dwFileDateMS, :dword,
      :dwFileDateLS, :dword
    )
  end
end

3. 完整的Ruby调用实现

按照C代码的逻辑一步步来,重点处理内存分配和指针传递:

target_path = "C:\\windows\\notepad.exe"
dw_handle = Fiddle::Pointer.malloc(Fiddle::SIZEOF_DWORD)

# 第一步:获取版本信息所需内存大小
dw_len = Win32VersionAPI.GetFileVersionInfoSizeW(target_path, dw_handle)
if dw_len == 0
  puts "获取版本大小失败,错误码:#{Fiddle.win32_last_error}"
  exit 1
end

# 第二步:分配内存存储版本数据
lp_data = Fiddle::Pointer.malloc(dw_len)
success = Win32VersionAPI.GetFileVersionInfoW(target_path, dw_handle.to_int, dw_len, lp_data)
unless success
  puts "读取版本信息失败,错误码:#{Fiddle.win32_last_error}"
  lp_data.free
  exit 1
end

# 第三步:提取VS_FIXEDFILEINFO结构体指针
# 注意:VerQueryValue需要传递指针的指针,所以先分配存储指针的内存
p_file_info_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
buf_len = Fiddle::Pointer.malloc(Fiddle::SIZEOF_UINT)
success = Win32VersionAPI.VerQueryValueW(lp_data, "\\", p_file_info_ptr, buf_len)
unless success
  puts "提取结构体失败,错误码:#{Fiddle.win32_last_error}"
  lp_data.free
  exit 1
end

# 将指针转换为结构体实例
file_info = Win32VersionAPI::VS_FIXEDFILEINFO.new(p_file_info_ptr.to_int)

# 解析版本号(高16位是主版本,低16位是次版本)
file_major = (file_info[:dwFileVersionMS] >> 16) & 0xFFFF
file_minor = file_info[:dwFileVersionMS] & 0xFFFF
file_build = (file_info[:dwFileVersionLS] >> 16) & 0xFFFF
file_revision = file_info[:dwFileVersionLS] & 0xFFFF

puts "Notepad.exe版本:#{file_major}.#{file_minor}.#{file_build}.#{file_revision}"

# 手动释放内存,避免泄漏
lp_data.free
p_file_info_ptr.free
buf_len.free
dw_handle.free

4. 避坑提醒

  • 必须用宽字符API:你的C代码用了L""宽字符串,Ruby里要对应调用W后缀的API,否则会出现字符串编码不匹配的问题。
  • 结构体布局丝毫不差:VS_FIXEDFILEINFO的成员顺序、类型必须和C定义完全一致,差一个成员或者类型错了都会读出垃圾数据。
  • 指针传递要准确:VerQueryValue需要的是指针的指针,所以得先分配一块存指针的内存,不能直接传结构体指针的地址。

内容的提问来源于stack exchange,提问作者cjones26

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:57:02