Windowsアプリその後

なんだかいじっていたら進化してしまったのでアップ。
Ruby1.8用。
DLのPtrDataオブジェクトのptrメソッドを呼ぶとなぜかコケるので、ウィンドウハンドルはポインタじゃなくint型にして強引に扱っている。
そんなもんか。

require "dl/import"
require "dl/struct"

module LIBC
  extend DL::Importable
  dlload "user32.dll"
  dlload "kernel32.dll"

  extern "int RegisterClassEx(int*)"
  extern "int DefWindowProc(int,int,int,int)"
  extern "int CreateWindowEx(int,char*,char*,int,int,int,int,int,int,int*,int*,int*)"
  extern "int GetMessage(int*,int,int,int)"
  extern "int TranslateMessage(int*)"
  extern "int DispatchMessage(int*)"
  extern "void PostQuitMessage(int)"
  extern "void* GetWindowLong(int,int)"
  extern "int SetWindowText(int, char*)"

  WndClassEx = struct [
    "int   cbSize",
    "int   style",
    "int*  lpfnWndProc",
    "int   cbClsExtra",
    "int   cbWndExtra",
    "int*  hInstance",
    "int*  hIcon",
    "int*  hCursor",
    "int  hbrBackground",
    "char* lpszMenuName",
    "char* lpszClassName",
    "int*  hIconSm"
  ]

  MSG = struct [
    "int hwnd",
    "int message",
    "int wParam",
    "int lParam",
    "int time",
    "int ptx",
    "int pty"
  ]
end

class Form
  @@control = {}

  def initialize
    @@control[self.class] = {}
    wndProc = DL::callback("IIIII") do |a,msg,wparam,lparam|
      case msg
        when 0x0111 # WM_COMMAND
          if wparam >> 16 == 0 then # BN_CLICKED
            self.__send__(@@control[self.class][lparam][0]+"_OnClick")
          end
          0
        when 2      # WM_DESTROY
          LIBC::postQuitMessage(0)
          0
        else
          LIBC::defWindowProc(a,msg,wparam,lparam)
      end
    end
    wndclass = LIBC::WndClassEx.malloc

    wndclass.cbSize = wndclass.size
    wndclass.style = 0
    wndclass.lpfnWndProc = wndProc
    wndclass.cbClsExtra = 0
    wndclass.cbWndExtra = 0
    wndclass.hInstance = nil
    wndclass.hIcon = nil
    wndclass.hCursor = nil
    wndclass.hbrBackground = 5 # COLOR_WINDOW
    wndclass.lpszMenuName = nil
    wndclass.lpszClassName = "testclass"
    wndclass.hIconSm = nil

    LIBC::registerClassEx(wndclass)

    # WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU 
    @hWnd = LIBC::createWindowEx(0,"testclass", "testwindow", 
                        0x10000000 | 0x00000000 | 0x00C00000 | 0x00080000,
                        0, 0, 200, 200, 0, nil, nil, nil)
    init
  end

  def addControl(c, name, caption, x, y, w, h)
    temp = c.new(@hWnd, caption, x, y, w, h)
    @@control[self.class][temp.hWnd] = [name, temp]
    instance_eval("def #{name}\n @@control[self.class][#{temp.hWnd}][1]\n end")
  end

  def messageLoop
    msg = LIBC::MSG.malloc
    loop do
      if LIBC::getMessage(msg, 0, 0, 0) == 0 then
        break
      end
      LIBC.translateMessage(msg)
      LIBC.dispatchMessage(msg)
    end
  end
end

class Button
  attr_reader :hWnd
  def initialize(hWnd, caption, x, y, w, h)
      @hWnd = LIBC::createWindowEx(0,"BUTTON",caption,
      0x10000000 | 0x40000000 | 0x00000001,# WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
      x,y,w,h,hWnd,nil,
      nil,#LIBC::getWindowLong(@hWnd, -6)
      nil);
  end

  def caption=(str)
    LIBC::setWindowText(@hWnd, str)
  end
end
# ↑ライブラリのコード

# ↓アプリケーションのコード
class Form1 < Form
  def init
    addControl(Button, "button1", "OK",     10, 10, 100, 20)
    addControl(Button, "button2", "Cancel", 10, 50, 100, 20)
  end

  def button1_OnClick
    button1.caption = "おらー"
  end
  def button2_OnClick
    button2.caption = "ぎゃー"
  end
end

a = Form1.new
a.messageLoop

こんな感じで使えるようにしていけば、なんだかWindowsアプリが簡単に作れそうな気がしてくる。
まあ、まともな開発環境になるまでには膨大なコードが必要で、そんな苦労するぐらいならVisualuRubyとかwxRubyとか使えよ、って話なんだけど。