Archive for 7月 14th, 2012

  1. Hello, Win32 GUI(Ruby) World!

    Posted on 7月 14th, 2012 by cx20

    Win32 GUI(Ruby)

    Win32 アプリケーションは Windows 標準 API である Win32 API を使用した Windows アプリケーションである。
    以下は Ruby による Win32 GUI アプリケーション の例となっている。
    なお、Win32 の構造体の定義に CStruct ライブラリを使用している。

    ソースコード

    require 'win32/api'
    require 'cstruct/win32struct'
     
    include Win32
     
    WS_VISIBLE      = 0x10000000
    WS_CAPTION      = 0x00C00000
    WS_SYSMENU      = 0x00080000
    WS_MINIMIZEBOX  = 0x00020000
    WS_MAXIMIZEBOX  = 0x00010000
    WS_THICKFRAME   = 0x00040000
    WS_OVERLAPPED   = 0x00000000
     
    WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
     
    WM_CREATE       = 0x0001
    WM_DESTROY      = 0x0002
    WM_PAINT        = 0x000F
    WM_CLOSE        = 0x0010
    WM_COMMAND      = 0x0111
     
    CS_VREDRAW      = 0x0001
    CS_HREDRAW      = 0x0002
    COLOR_WINDOW    = 5
     
    CW_USEDEFAULT   = -2147483648 #0x80000000
     
    SW_SHOW         = 5
    SW_SHOWDEFAULT  = 10
     
    Memset          = API.new('memset', 'PLL', 'L', 'msvcrt')
    LocalAlloc      = API.new('LocalAlloc', 'LL', 'L', 'kernel32')
    GetModuleHandle = API.new('GetModuleHandle', 'P', 'L', 'kernel32')
    RegisterClass   = API.new('RegisterClass', 'P', 'L', 'user32')
    RegisterClassEx = API.new('RegisterClassEx', 'P', 'L', 'user32')
    CloseWindow     = API.new('CloseWindow', 'L', 'B', 'user32')
    CreateWindowEx  = API.new('CreateWindowEx', 'LPPLIIIILLLL', 'L', 'user32')
    ShowWindow      = API.new('ShowWindow', 'LL', 'B', 'user32')
    UpdateWindow    = API.new('UpdateWindow', 'L', 'B', 'user32')
    BeginPaint      = API.new('BeginPaint', 'LP', 'L', 'user32')
    EndPaint        = API.new('EndPaint', 'LP', 'L', 'user32')
    PostQuitMessage = API.new('PostQuitMessage', 'I', 'V', 'user32')
    DefWindowProc   = API.new('DefWindowProc', 'LLLL', 'L', 'user32')
    GetMessage      = API.new('GetMessage', 'PLII', 'L', 'user32')
    DispatchMessage = API.new('DispatchMessage', 'P', 'L', 'user32')
    TranslateMessage = API.new('TranslateMessage', 'P', 'B', 'user32')
    TextOut         = API.new('TextOut', 'LLLPL', 'B', 'gdi32' );
     
    class POINT < Win32Struct
        LONG    :x
        LONG    :y
    end
     
    class MSG < Win32Struct
        uint32  :hwnd
        UINT    :message
        WPARAM  :wParam
        LPARAM  :lParam
        DWORD   :time
        POINT   :pt
    end
     
    class WNDCLASSA < Win32Struct
        UINT    :style
        uint32  :lpfnWndProc
        int32   :cbClsExtra
        int32   :cbWndExtra
        HINSTANCE   :hInstance
        HICON   :hIcon
        HCURSOR :hCursor
        HBRUSH  :hbrBackground
        LPCSTR  :lpszMenuName
        LPCSTR  :lpszClassName
    end
     
    class RECT < Win32Struct
        LONG    :left
        LONG    :top
        LONG    :right
        LONG    :bottom
    end
     
    class PAINTSTRUCT < Win32Struct
        HDC     :hdc
        BOOL    :fErase
        RECT    :rcPaint
        BOOL    :fRestore
        BOOL    :fIncUpdate
        BYTE    :rgbReserved,[32]
    end
     
    @window_proc = API::Callback.new('LLLL', 'I') do |hwnd, msg, wparam, lparam|
        ret = 0
        strMessage = "Hello, Win32 GUI(Ruby) World!"
        case msg
        when WM_PAINT
            ps  = PAINTSTRUCT.new
            hdc = BeginPaint.call(hwnd, ps.data)
            TextOut.call(hdc, 0, 0, strMessage, strMessage.length);
            EndPaint.call(hwnd, ps.data)
        when WM_DESTROY
            PostQuitMessage.call(0)
        else 
            ret = DefWindowProc.call(hwnd, msg, wparam, lparam)
        end
        ret
    end
     
    def alloc_string_buffer init_string, max_length
        buffer_pointer = LocalAlloc.call(0, max_length)
        buffer_addr = buffer_pointer
        Memset.call(buffer_addr, 0, max_length)
     
        init_string.each_byte do |byte|  
            Memset.call(buffer_pointer, byte, 1)
            buffer_pointer += 1
        end  
        buffer_addr
    end
     
    def WinMain instance, cmd_show
        msg = MSG.new
     
        wc = WNDCLASSA.new
        wc.style         = 0
        wc.lpfnWndProc   = @window_proc.address
        wc.cbClsExtra    = 0
        wc.cbWndExtra    = 0
        wc.hInstance     = instance
        wc.hIcon         = 0
        wc.hCursor       = 0
        wc.hbrBackground = (COLOR_WINDOW+1)
        wc.lpszMenuName  = 0
        wc.lpszClassName = alloc_string_buffer 'helloWindow', 256
     
        RegisterClass.call(wc.data)
     
        hwnd = CreateWindowEx.call(
            0,
            "helloWindow",
            "Hello, World!",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            640,
            480,
            0,
            0,
            wc.hInstance,
            0
        )
     
        ShowWindow.call(hwnd, cmd_show)
        UpdateWindow.call(hwnd)
     
        while(GetMessage.call(msg.data, 0, 0, 0) > 0)
            TranslateMessage.call(msg.data)
            DispatchMessage.call(msg.data)
        end
     
    end
     
    def main
        instance = GetModuleHandle.call(0)
        WinMain instance, SW_SHOW
    end
     
    main

    ライブラリ導入方法

    C:¥> gem install cstruct

    実行方法

    C:¥> ruby hello.rb

    実行結果

    +------------------------------------------+
    |Hello, World!                    [_][~][X]|
    +------------------------------------------+
    |Hello, Win32 GUI(Ruby) World!             |
    |                                          |
    |                                          |
    |                                          |
    |                                          |
    |                                          |
    |                                          |
    |                                          |
    |                                          |
    |                                          |
    +------------------------------------------+