代码之家  ›  专栏  ›  技术社区  ›  nlaq

Ruby win32 api接口

  •  6
  • nlaq  · 技术社区  · 15 年前

    我需要在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')
    

    但第一个在“call”方法期间出错,第二个由于“call”方法调用中指定的参数数量错误而无法运行。有没有办法在不使用C++中创建外部模块的情况下公开这个API函数?

    谢谢

    3 回复  |  直到 15 年前
        1
  •  4
  •   bltxd    15 年前

    诀窍是使用“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
    
        2
  •  0
  •   robson3.14    15 年前

    SHAppBarMessage有两个参数:DWORD和指向APPBARDATA的指针,
    因此可以这样宣布:

    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)

    但我不认识鲁比,所以也许我弄错了。。。

        3
  •  0
  •   Mike Woodhouse    15 年前

    我想你得调查这件事 String#pack 获取您的 APPBARDATA struct 填写正确。

    看《鹤嘴锄》这本书 section on Win32 and Ruby (向下滚动到Win32API类定义)。

    如前所述,你将使用一个“P”参数,并通过一个正确打包的 String (或 一串 s) 进入函数。

    或者,如果你有一点时间调查,你可能想看看FFI图书馆,它似乎以一种更友好的方式做每件事。我没有直接的经验,但试着看看