2009-07-29 18 views
6

Necesito acceder a algunas funciones de la biblioteca win32 en ruby. He encontrado información muy escasa en la clase de Win32API en línea, así que estoy preguntando aquí.Ruby win32 api interface

sé que se puede hacer algo como esto:

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

Pero me parece que no puede ser capaz de llamar a esta función con los enlaces actuales Win32:

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

El el problema está en su prototipo:

UINT_PTR SHAppBarMessage(  
    DWORD dwMessage, 
    PAPPBARDATA pData 
); 

I'll be abl e para usar los enlaces de rubí win32 para obtener el tipo de retorno y el primer parámetro, sin embargo, el segundo espera una estructura. La definición de la estructura es la siguiente:

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

Me trataron de definir este método API usando ambos:

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

y

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

pero el primero segfaults durante la "llamada "método y el segundo no se ejecuta debido a la cantidad incorrecta de argumentos especificados en la invocación del método" llamada ". ¿Hay alguna forma de exponer esta función api sin recurrir a la creación de un módulo externo en C++?

Gracias.

Respuesta

0

SHAppBarMessage toma dos parámetros: un valor DWORD y un puntero a APPBARDATA,
por lo que shuold ser declarado así:

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

entonces llamada:

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

Pero no lo sé Ruby, entonces tal vez estoy equivocado ...

0

Creo que tendrás que investigar el método String#pack para obtener su APPBARDATA struct lleno correctamente.

Consulte el libro "Pickaxe" section on Win32 and Ruby (desplácese hacia abajo a la definición de la clase Win32API).

Como ya se vio, usará un argumento 'P' y pasará un String (o String s) correctamente empaquetado a la función.

Alternativamente, si tiene un poco de tiempo para investigar, es posible que desee consultar la biblioteca FFI, que parece hacer todo de una manera bastante más amigable. No tengo ninguna experiencia directa, pero intente buscar en

4

El truco es utilizar 'P' como un especificador de formato para todos los argumentos de puntero. Tendrás que proporcionar una cadena como el área apuntada.

Por supuesto, deberá asegurarse de que estas cuerdas tengan el tamaño correcto esperado, de lo contrario, sucederán cosas malas.

Se pueden crear directamente estas cadenas

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

o utilizar el más civilizado Array#pack

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

Espero que esto ayude.


Los siguientes trabajos en mi caja de XP con un viejo rubí 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 
Cuestiones relacionadas