w32apis.rb

ちょっと改造。

require 'Win32API'
class W32APIs
  attr_reader :apis
  attr_accessor :incpath
  include Enumerable
  @@h = {
    'int'=>'I',
#..snip..
    'WCHAR'=>'H',
  }

  def each(&block)
    @apis.each{|k,v| yield k,v}
  end

  def initialize(incs={'winuser.h'=>'user32'},incpath=['/usr/include/w32api'])
    @apis = { }
    @incpath = incpath
    add_apis(incs)
  end

  def add_apis(incs)
    incs.each do |inc, dll|
      path = @incpath.find{|p|File.exist?(p+'/'+inc)}
      open(path + '/' + inc) do |f|
        f.each do |l|
          if /^(\w.+)\((.+)\)\s*;\s*$/ =~ l
            ret, arg = $1, $2
            next if /^typedef/ =~ ret
            ret.gsub!(/WIN[A-Z]*APIV?(\W)/){$1}
            ret.sub!(/^\s*(.+)\s*$/){$1}
            ret.sub!(/^extern\s*/,'')
            next if 'DECLARE_HANDLE' == ret
            rets = ret.split
            args = arg.split(/\s*,\s*/)
            arg_str = ''
            args.each do |e|
              e.gsub!(/^const/i,'')
              e.sub!(/^\s+/,'')
              e.sub!(/\s+\w+$/,'')
              unless @@h.key?(e)
                if /\s*\*\s*$/ =~ e
                  @@h[e] = 'P' #FixMe
                elsif /^LP/ =~ e
                  @@h[e] = 'P' #FixMe
                else
                  @@h[e] = '-'
                end
              end
              arg_str << @@h[e]
            end
            @apis[rets[1]] = [nil, dll, arg_str, @@h[rets[0]], l]
          end
        end
      end
    end
  end

  def method_missing(name, *arg)
    n = name.to_s 
    a = @apis[n]
    unless a[0]
      a[0] = Win32API.new(a[1], n, a[2], a[3])
    end
   a[0].call(*arg)
  end
end
if $0 == __FILE__
  w = W32APIs.new
  w.MessageBoxA(0, "Hello, World!", "Title", 0)
end

使い方。

【新規作成/APIグループの追加】
w = W32APIs.new
w.add_apis('rapi.h'=>'rapi.dll')
(デフォルトでは user32.dll に入っている api しかサーチしてない)

APIのコール】
w.MessageBoxA(0, "Hellow, World!", "Title", 0)
#初めて↑コールされた時点でmethod_missingで Win32API.new されて定義される

【EnumerableでAPIを探す】
apis = w.select{|k,v|/MessageBox/=~k/}
api = w.find{|k,v|/MessageBox/=~k}

【その他】
irbとか使ってて、ちょっと win32api の検索をしたり、引数を調べたり、動作確認をしたりとかしたい時とか便利。

irb(z:/ara/):004:0> require 'w32apis';w = W32APIs.new;1
R:1
irb(z:/ara/):005:0> w.add_apis('rapi.h'=>'rapi.dll')
R:{"rapi.h"=>"rapi.dll"}
irb(z:/ara/):006:0> w.select{|k,v|/CeG/=~k}
R:[["CeGetFileTime", [nil, "rapi.dll", "PPPP", nil, "STDAPI_(BOOL) CeGetFileTime (HANDLE, LPFILETIME, LPFILETIME, LPFILETIME); \n"]], ["CeGetLastError", [nil, "rapi.dll", "V", nil, "STDAPI_(DWORD) CeGetLastError (void);\n"]]]
irb(z:/ara/):007:0> w.select{|k,v|/MoveW/=~k}
R:[["MoveWindow", [nil, "user32", "LIIIIL", "L", "WINUSERAPI BOOL WINAPI MoveWindow(HWND,int,int,int,int,BOOL);\n"]]]
irb(z:/ara/):008:0> w.select{|k,v|/MessageBox/=~k}
R:[["MessageBoxA", [nil, "user32", "LPPL", "I", "WINUSERAPI int WINAPI MessageBoxA(HWND,LPCSTR,LPCSTR,UINT);\n"]], ["MessageBoxExA", [nil, "user32", "LPPLH", "I", "WINUSERAPI int WINAPI MessageBoxExA(HWND,LPCSTR,LPCSTR,UINT,WORD);\n"]], ["MessageBoxW", [nil, "user32", "LPPL", "I", "WINUSERAPI int WINAPI MessageBoxW(HWND,LPCWSTR,LPCWSTR,UINT);\n"]], ["MessageBoxExW", [nil, "user32", "LPPLH", "I", "WINUSERAPI int WINAPI MessageBoxExW(HWND,LPCWSTR,LPCWSTR,UINT,WORD);\n"]], ["MessageBoxIndirectA", [nil, "user32", "P", "I", "WINUSERAPI int WINAPI MessageBoxIndirectA(CONST MSGBOXPARAMSA*);\n"]], ["MessageBoxIndirectW", [nil, "user32", "P", "I", "WINUSERAPI int WINAPI MessageBoxIndirectW(CONST MSGBOXPARAMSW*);\n"]]]
irb(z:/ara/):009:0> w.MessageBoxA(0,"Hello","title",0)
R:1