Ruby Win32 API интерфейс - PullRequest
       16

Ruby Win32 API интерфейс

6 голосов
/ 29 июля 2009

Мне нужно получить доступ к нескольким функциям библиотеки win32 в ruby. Я нашел крайне скудную информацию о классе Win32API онлайн, поэтому я спрашиваю здесь.

Я знаю, что вы можете сделать что-то вроде этого:

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

Но я не могу вызвать эту функцию с текущими привязками win32:

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

Проблема в его прототипе:

UINT_PTR SHAppBarMessage(      
    DWORD dwMessage,
    PAPPBARDATA pData
);

Я смогу использовать привязки win32 ruby ​​для получения возвращаемого типа и первого параметра, однако второй ожидает структуру. Определение структуры выглядит следующим образом:

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

Я попытался определить этот метод API, используя оба:

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

и

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

, но первый из них завершается ошибкой во время метода "call", а второй не запускается из-за неправильного количества аргументов, указанных в вызове метода "call". Есть ли способ предоставить эту функцию API, не прибегая к созданию внешнего модуля в C ++?

Спасибо.

Ответы [ 3 ]

4 голосов
/ 25 января 2010

Хитрость заключается в том, чтобы использовать 'P' в качестве спецификатора формата для всех аргументов указателя . Вам нужно будет указать строку как указанную область.

Конечно, вы должны убедиться, что эти строки имеют правильный ожидаемый размер, иначе могут случиться плохие вещи.

Вы можете напрямую создать эти строки

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

или используйте более цивилизованный Array#pack

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

Надеюсь, это поможет.


На моем XP box со старым 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
0 голосов
/ 30 июля 2009

Я думаю, вам придется исследовать метод String#pack, чтобы правильно заполнить APPBARDATA struct.

См. Раздел "1005 *" книги "Кирка" по Win32 и Ruby (прокрутите вниз до определения класса Win32API).

Таким образом, как уже было обнаружено, вы будете использовать аргумент 'P' и передадите правильно упакованный String (или String s) в функцию.

В качестве альтернативы, если у вас есть немного времени для исследования, вы можете посмотреть библиотеку FFI, которая, кажется, делает все более дружественным образом. У меня нет прямого опыта, но попробуйте посмотреть на

0 голосов
/ 30 июля 2009

SHAppBarMessage принимает два параметра: DWORD и указатель на APPBARDATA,
поэтому он должен быть объявлен так:

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

затем позвонил:

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

Но я не знаю Руби, так что, возможно, я ошибаюсь ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...