2009-07-29 18 views
6

Ho bisogno di accedere ad alcune funzioni della libreria win32 in ruby. Ho trovato informazioni estremamente sparse sulla classe Win32API online, quindi sto chiedendo qui.Ruby interfaccia win32 api

So che si può fare qualcosa di simile:

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

Ma io non riesco a essere in grado di richiamare questa funzione con le associazioni win32 attuali:

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

Il problema è nel suo prototipo:

UINT_PTR SHAppBarMessage(  
    DWORD dwMessage, 
    PAPPBARDATA pData 
); 

sarò abl e usare i vincoli ruby ​​win32 per afferrare il tipo di ritorno e il primo parametro, tuttavia, il secondo si aspetta una struttura. La definizione della struttura è la seguente:

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

Ho provato a definire questo metodo api utilizzando entrambi:

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

e

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

ma il primo segfaults durante la "chiamata "metodo e il secondo non riesce a funzionare a causa della quantità errata di argomenti specificata nel richiamo del metodo" call ". C'è un modo per esporre questa funzione API senza ricorrere alla creazione di un modulo esterno in C++?

Grazie.

risposta

0

SHAppBarMessage accetta due parametri: un valore DWORD e un puntatore a APPBARDATA,
quindi shuold essere dichiarate così:

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

allora si chiamava:

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

Ma non so Ruby, quindi forse mi sbaglio ...

0

Penso che dovrai studiare il metodo String#pack per ottenere il tuo APPBARDATA struct è stato riempito correttamente.

Vedere il libro "Piccone" section on Win32 and Ruby (scorrere fino alla definizione della classe Win32API).

Così come si è già visto, si utilizzerà un argomento "P" e si passerà uno String (o String s) correttamente inserito nella funzione.

In alternativa, se si ha un po 'di tempo per indagare, si potrebbe voler guardare la libreria FFI, che sembra fare tutto in un modo piuttosto più amichevole. Non ho esperienza diretta, ma prova a guardare

4

Il trucco è quello di utilizzare 'P' come di formato per tutti gli argomenti puntatore. Dovrai fornire una stringa come area puntata.

Ovviamente dovrete assicurarvi che queste stringhe abbiano la dimensione giusta prevista altrimenti accadranno cose brutte.

È possibile creare direttamente queste stringhe

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

o utilizzare il più civile Array#pack

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

Spero che questo aiuti.


Le seguenti opere sulla mia scatola di XP con un vecchio 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 
Problemi correlati