2009-07-29 5 views
6

J'ai besoin d'accéder à quelques fonctions de la bibliothèque win32 dans ruby. J'ai trouvé des informations extrêmement clairsemées sur la classe Win32API en ligne, donc je demande ici.Ruby win32 api interface

Je sais que vous pouvez faire quelque chose comme ceci:

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

Mais je ne peux pas sembler être en mesure d'appeler cette fonction avec les liaisons win32 actuelles:

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

Le problème est dans son prototype:

UINT_PTR SHAppBarMessage(  
    DWORD dwMessage, 
    PAPPBARDATA pData 
); 

Je serai abl e pour utiliser les bindings ruby ​​win32 pour saisir le type de retour et le premier paramètre, cependant, le second attend une structure. La définition de la structure est la suivante:

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

J'ai essayé de définir cette méthode api utilisant à la fois:

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

et

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

mais le premier segfaults au cours de la « appel "méthode et la seconde ne parvient pas à s'exécuter en raison de la mauvaise quantité d'arguments spécifiés dans l'appel de la méthode" appel ". Est-il possible d'exposer cette fonction api sans avoir recours à la création d'un module externe en C++?

Merci.

Répondre

0

SHAppBarMessage prend deux paramètres: un DWORD et un pointeur vers APPBARDATA,
il shuold être ainsi déclaré:

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

alors appelé:

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

Mais je ne sais pas Ruby, alors peut-être que je me trompe ...

0

Je pense que vous devrez étudier la méthode String#pack pour obtenir votre APPBARDATA struct rempli correctement. Voir le livre "Pickaxe" section on Win32 and Ruby (faites défiler jusqu'à la définition de classe Win32API).

Comme déjà détecté, vous utiliserez un argument 'P' et vous passerez un String (ou String s) correctement emballé dans la fonction. Alternativement, si vous avez un peu de temps pour enquêter, vous voudrez peut-être regarder la bibliothèque FFI, qui semble tout faire d'une manière plutôt conviviale. Je n'ai pas l'expérience directe, mais essayez de regarder

4

L'astuce consiste à utiliser 'P' comme spécificateur de format pour tous les arguments pointeur. Vous devrez fournir une chaîne comme zone pointée.

Bien sûr, vous devrez vous assurer que ces chaînes ont la bonne taille attendue, sinon de mauvaises choses se produiront.

Vous pouvez directement créer ces chaînes

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

ou utiliser le plus civilisé 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.


Les travaux suivants sur ma boîte de XP avec un vieux rubis 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 
Questions connexes