Ruby win32 api interface

后端 未结 3 1502
囚心锁ツ
囚心锁ツ 2020-12-06 03:12

I need to access a few functions of the win32 library in ruby. I have found extremely sparse information on the Win32API class online, so I\'m asking here.

I know th

3条回答
  •  广开言路
    2020-12-06 03:34

    The trick is to use 'P' as a format specifier for all pointer arguments. You'll have to provide a string as the pointed-to area.

    Of course you'll have to make sure these strings have the correct expected size otherwise bad things will happen.

    You can directly create these strings

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

    or use the more civilized Array#pack

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

    Hope this helps.


    The following works on my XP box with an old ruby 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
    

提交回复
热议问题