2009-07-29 56 views
6

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

我知道,你可以做这样的事情:

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 
); 

我会abl e使用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++中创建一个外部模块?

谢谢。

回答

0

SHAppBarMessage有两个参数:一个DWORD和指针APPBARDATA,
所以shuold因而被宣告:

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

然后叫:

msg_id = 1 
app_bar_data = "properly initalized binary string" #should have sizeof(APPBARDATA) bytes 
app_bar_msg.call(msg_id, app_bar_data)

但我不知道红宝石,所以也许我错了...

0

我认为你将不得不调查String#pack方法得到你的APPBARDATA struct正确填写。

请参阅“镐”书section on Win32 and Ruby(向下滚动到Win32API类定义)。

因此,如果您已经发现,您将使用'P'参数,并且您会将正确填充的String(或String s)传递到函数中。另外,如果你有一点时间去调查,你可能想看看FFI图书馆,它似乎以一种更友好的方式来处理所有事情。我没有直接经验,但试图通过JRuby团队的查尔斯·纳特,那里的的声明,C的结构更加美好的方式为例看着

4

诀窍是使用“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') 

希望这有助于。


在我的XP对话框下的作品与旧的红宝石1.8.2:

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