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
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.
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 takes two parameters: a DWORD and a pointer to APPBARDATA,
so it shuold be declared thus:
app_bar_msg = Win32API.new('shell32', 'SHAppBarMessage', ['L', 'P'], 'L')
then called:
msg_id = 1 app_bar_data = "properly initalized binary string" #should have sizeof(APPBARDATA) bytes app_bar_msg.call(msg_id, app_bar_data)
But I don't know Ruby, so maybe I am mistaken...
I think you'll have to investigate the String#pack
method to get your APPBARDATA struct
filled correctly.
See the "Pickaxe" book section on Win32 and Ruby (scroll down to the Win32API class definition).
So as already spotted, you'll use a 'P' argument and you'll pass a properly-packed String
(or String
s) into the function.
Alternatively, if you have a little time to investigate, you may want to look at the FFI library, which seems to do everything in a rather more friendly way. I have no direct experience, but try looking at