w32apis.rb

よくRubyから win32apiを呼び出して使用する。

しかし、その度に Win32API.newするのが面倒なので、下記の様なラッパーを使ってる。

こうやっとくと、「require 'w32apis';w=W32APIs.new」とかするだけで、w.MessageBoxA(0,"Hello, World.","",0) とかして APIを呼び出せるんで便利。

#!/usr/bin/ruby
# -*- coding: utf-8 -*-
require 'Win32API'
class W32APIs
  attr_reader :apis
  def initialize
    @file = '/usr/include/w32api/winuser.h'
    @h = {
      'int'=>'I',
      'HWND'=>'L',
      'WORD'=>'H', #だっけ?
      'SHORT'=>'H', #だっけ?
      'LPCSTR'=>'P',
      'void'=>'V',
      'HBRUSH'=>'L',
      'HANDLE'=>'P', #だっけ? L? (void*)な筈だが… 
      'LPWSTR'=>'P',
      'HHOOK'=>'L',
      'HWINEVENTHOOK'=>'L',
      'VOID'=>'V',
      'LONG_PTR'=>'P',
      'ULONG_PTR'=>'P',
      'HBITMAP'=>'L',
      'HWINSTA'=>'L',
      'HDESK'=>'L',
      'DWORD'=>'L',#だっけ?
      'HDEVNOTIFY'=>'L',
      'INT'=>'I',
      'HACCEL'=>'L',
      'LPARAM'=>'L',
      'LPSTR'=>'P',
      'LPCWSTR'=>'P',
      'UINT'=>'L',
      'HMENU'=>'L',
      'HICON'=>'L',
      'HDC'=>'L',
      'HMONITOR'=>'L',
      'LRESULT'=>'L',
      'HPOWERNOTIFY'=>'L',
      'LONG'=>'L',
      'long'=>'L',
      'HDWP'=>'L',
      'HCURSOR'=>'L',
      'HKL'=>'L', #だっけ?
      'BOOL'=>'L',
      'ATOM'=>'H', #だっけ? とりあえず WORD で typedef されてるけど。 
      'BYTE'=>'C', #だっけ?
      'CHAR'=>'C', #だっけ?
      'PALTTABINFO'=>'P',
      'PBOOL'=>'P',
      #'PBSMINFO'=>'-',
      'PBYTE'=>'P',
      'PCOMBOBOXINFO'=>'P',
      'PCRAWINPUTDEVICE'=>'P',
      'PCURSORINFO'=>'P',
      'PCVOID'=>'P',
      'PDEVMODEA'=>'P',
      'PDEVMODEW'=>'P',
      'PDISPLAY_DEVICEA'=>'P',
      'PDISPLAY_DEVICEW'=>'P',
      'PDWORD'=>'P',
      'PFLASHWINFO'=>'P',
      'PICONINFO'=>'P',
      'PLASTINPUTINFO'=>'P',
      'PMENUBARINFO'=>'P',
      #'POINT'=>'-',
      'PRAWINPUT'=>'P',
      'PRAWINPUTDEVICE'=>'P',
      'PRAWINPUTDEVICELIST'=>'P',
      #'PROPENUMPROCA'=>'-',
      #'PROPENUMPROCEXA'=>'-',
      #'PROPENUMPROCEXW'=>'-',
      #'PROPENUMPROCW'=>'-',
      'PSCROLLBARINFO'=>'P',
      'PSECURITY_DESCRIPTOR'=>'P',
      'PSECURITY_INFORMATION'=>'P',
      'PTITLEBARINFO'=>'P',
      'PUINT'=>'P',
      'PVOID'=>'P',
      'PWINDOWINFO'=>'P',
      'UINT_PTR'=>'P',
      'WCHAR'=>'H',
    }
    @apis = { }
    open(@file) 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].to_sym] = [nil, 'user32', arg_str, @h[rets[0]], l] # FixMe (user32?)
        end
      end
    end
  end

  def method_missing(name, *arg)
    a = @apis[name]
    unless a[0]
      a[0] = Win32API.new(a[1], name.to_s, 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

私の使用するPCには cygwin やら VisualC++2008 ExpressEdition やらが入っているので、そのヘッダファイルを利用して、半自動で Win32API をコールしてやろうという…