Ruby win32 api接口

我需要在ruby中访问win32库的一些函数。 我在网上找到了关于Win32API类的非常稀少的信息,所以我在这里问。

我知道你可以这样做:

function = Win32API.new('user32','MessageBox',['L', 'P', 'P', 'L'],'I') 

但我似乎无法使用当前的win32绑定调用此函数:

http://msdn.microsoft.com/en-us/library/bb762108%28VS.85%29.aspx

问题在于它的原型:

 UINT_PTR SHAppBarMessage( DWORD dwMessage, PAPPBARDATA pData ); 

我将能够使用win32 ruby​​绑定来获取返回类型和第一个参数,但是,第二个参数需要一个结构。 结构的定义如下:

 typedef struct _AppBarData { DWORD cbSize; HWND hWnd; UINT uCallbackMessage; UINT uEdge; RECT rc; LPARAM lParam; } APPBARDATA, *PAPPBARDATA; 

我尝试使用两者来定义这个api方法:

 api = Win32API.new('shell32','SHAppBarMessage',['L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'],'I') 

 api = Win32API.new('shell32','SHAppBarMessage',['L', 'LLLLLLLL'],'I') 

但是第一个在“调用”方法期间发生了段错误,而第二个由于“调用”方法调用中指定的参数数量错误而无法运行。 有没有办法公开这个api函数而不需要在C ++中创建外部模块?

谢谢。

诀窍是使用’P’作为所有指针参数的格式说明符 。 你必须提供一个字符串作为指向区域。

当然,你必须确保这些字符串具有正确的预期大小,否则会发生不好的事情。

您可以直接创建这些字符串

 # Mostly useful when the area will be totally overwritten pointed_to_area = "\0" * n 

或者使用更文明的Array#pack

 # Allows you to control how ruby values get encoded in the buffer pointed_to_area = [1, 2, 3, 4].pack('SsLI') 

希望这可以帮助。


以下适用于我的带有旧ruby 1.8.2的XP机箱:

 require 'Win32API' module Win32 # This method is only here for test purposes # Be careful to use the ascii version FindWindow = Win32API.new('user32', 'FindWindowA', ['P', 'P'], 'L') def self.findWindow(lpClassName, lpWindowName) h = FindWindow.call(lpClassName, lpWindowName) raise "FindWindow failed" if h == 0 h end # From winddef.h RECT = Struct.new(:left, :top, :right, :bottom) RECT.class_eval do def pack [left, top, right, bottom].pack('l4') end def self.unpack(s) new(*s.unpack('l4')) end end # From shellapi.h APPBARDATA = Struct.new(:cbSize, :hWnd, :uCallbackMessage, :uEdge, :rc, :lParam) APPBARDATA.class_eval do def pack unless rc.is_a? RECT raise ArgumentError, ":rc must be an instance of Win32::RECT, got #{rc.inspect}" end # DWORD + HWND + UINT + UINT + RECT + LPARAM cbSize = 4 + 4 + 4 + 4 + 16 + 4 [cbSize, hWnd, uCallbackMessage, uEdge, rc.pack, lParam].pack('L2I2a16L') end def self.unpack(s) tmp = self.new(*s.unpack('L2I2a16L')) tmp.rc = RECT.unpack(tmp.rc) tmp end end SHAppBarMessage = Win32API.new('shell32', 'SHAppBarMessage', ['L', 'P'], 'L') # Calls SHAppBarMessage and returns the altered APPBARDATA def self.shAppBarMessage(dwMessage, appBarData) s = appBarData.pack ok = (SHAppBarMessage.call(dwMessage, s) != 0) raise "SHAppBarMessage failed" unless ok APPBARDATA.unpack(s) end ABM_NEW = 0x00000000 ABM_REMOVE = 0x00000001 ABM_QUERYPOS = 0x00000002 ABM_SETPOS = 0x00000003 ABM_GETSTATE = 0x00000004 ABM_GETTASKBARPOS = 0x00000005 ABM_ACTIVATE = 0x00000006 ABM_GETAUTOHIDEBAR = 0x00000007 ABM_SETAUTOHIDEBAR = 0x00000008 ABM_WINDOWPOSCHANGED = 0x00000009 ABM_SETSTATE = 0x0000000a ABE_LEFT = 0 ABE_TOP = 1 ABE_RIGHT = 2 ABE_BOTTOM = 3 end if __FILE__ == $0 require 'test/unit' class SHAppBarMessageTest < Test::Unit::TestCase include Win32 def test_pack_unpack a = APPBARDATA.new(-1, 0, 0, ABE_TOP, RECT.new(1, 2, 3, 4), 0) b = APPBARDATA.unpack(a.pack) a.cbSize = b.cbSize assert_equal(a.values, b.values) end def test_simple_pos_query h = Win32.findWindow("Shell_TrayWnd", nil) a = APPBARDATA.new(-1, 0, 0, ABE_TOP, RECT.new(0, 0, 0, 0), 0) result = Win32.shAppBarMessage(ABM_GETTASKBARPOS, a) assert(result.rc.left < result.rc.right) assert(result.rc.top < result.rc.bottom) puts result.rc.inspect end end end 

SHAppBarMessage有两个参数:一个DWORD和一个指向APPBARDATA的指针,
所以它如此宣布:

  app_bar_msg = Win32API.new('shell32','SHAppBarMessage',['L','P'],'L') 

然后叫:

  msg_id = 1
 app_bar_data =“正确初始化的二进制字符串”#应该有sizeof(APPBARDATA)字节
 app_bar_msg.call(msg_id,app_bar_data) 

但我不认识Ruby,所以也许我错了……

我认为你必须调查String#pack方法以正确填充你的APPBARDATA struct

请参阅Win32和Ruby上的“Pickaxe”一书部分 (向下滚动到Win32API类定义)。

因此,正如已经发现的那样,您将使用’P’参数,并将正确打包的String (或String )传递给函数。

或者,如果您有一点时间进行调查,您可能需要查看FFI库,它似乎以更友好的方式执行所有操作。 我没有直接的经验,但试着看

  • Charles Nutter对JRuby团队的最初公告 ,其中有一个更好的方式来声明C结构。
  • Sun的Ruby FFI主页