RustでWindows API使用!懐かしいウィンドウ作成

開発
200 DegreesによるPixabayからの画像

※この記事にはプロモーションが含まれています。

今回は、近年話題の言語Rustを用いて、Windowsのウィンドウを作成してみたいと思います。ゆくゆくは、Rustでガンガンロジック部分を書いて、高速・効率的なプログラムを目指したいところです。

スポンサーリンク
スポンサーリンク

Rustとは

下記の特徴を持つ、2015年に1stバージョンがリリースされた、比較的新しい言語です。

  • C言語並みに低レイヤを扱える
  • CPUやメモリの効率が良い
  • 所有権の概念や強力な型チェックの導入、並列処理を安全に実装可能
  • イテレータ・クロージャなど最新の表現力を保有

有名な製品プロジェクトでも、C言語からRustへの乗り換えを耳にすることが多くなってきました。

環境作成

Windows 10上に、Rustの開発環境を作成します。

依存モジュール

WindowsでRustを利用する場合、下記どちらかのモジュールをインストールしておく必要があります。

Rustインストール

Rustの公式サイトから、RUSTUP-INIT.exeをダウンロードして実行します。ユーザープロファイルフォルダ下に配置されるので、管理者権限は必要ありません。PATHも自動的に通してくれます。

また、開発を容易にするために、Visual Studio Codeに拡張モジュールもインストールしておくと便利です。

Rustのプロジェクト

Rustは、newコマンドでプロジェクトのひな型を作成することができます。また、ビルド・実行を行うにはrunを用います。

cargo new test_window

cd test_window

cargo run

release版をビルド・実行したい場合は、cargo run –releaseとします。これは、buildコマンドでも同様です。

Windows API使用

Cargo.tomlには、以下の様にdependenciesを登録しています。

[dependencies]
winapi = { version = "0.3.9", features = ["winuser"] }

ここのバージョンは、”cargo search winapi”って感じで、検索できます。

また、main.rsはwinapi-rsMSDNを参考にしながら、下記の通り記述しました。ウィンドウを作成し、画面に文字列を表示して、×ボタンかAlt+F4キーを受け付けると終了します。Windows内部はUTF-16なので、文字列を加工しています。

use winapi::shared::windef::*;
use winapi::shared::minwindef::*;
use winapi::um::winuser::*;
use winapi::um::winnt::*;
use winapi::um::wingdi::*;

fn encode(source: &str) -> Vec<u16> {
    source.encode_utf16().chain(Some(0)).collect()
}

pub unsafe extern "system" fn wnd_proc(hwnd :HWND, msg :UINT, wparam :WPARAM, lparam :LPARAM) -> LRESULT
{
    let text = encode("あいうえお");
    let text_len = text.len() as i32 - 1;
    let mut hdc = 0 as HDC;
    let mut pt = PAINTSTRUCT{
        hdc: 0 as HDC,
        fErase: FALSE as BOOL,
        rcPaint: RECT{left:0, top:0, right:0, bottom:0},
        fRestore: FALSE as BOOL,
        fIncUpdate: FALSE as BOOL,
        rgbReserved: [0; 32]
    };

    if msg == WM_DESTROY {
        PostQuitMessage(0);
    }else if msg == WM_PAINT {
        hdc = BeginPaint(hwnd , &mut pt);
        TextOutW(hdc, 10, 10, text.as_ptr(), text_len);
        EndPaint(hwnd , &mut pt);
    }
    return DefWindowProcW(hwnd, msg, wparam, lparam);
}

fn main() {
    unsafe {
        let class_name = encode("test_window");
        let wnd = WNDCLASSW {
            style: 0,
            lpfnWndProc: Some(wnd_proc), 
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: 0 as HINSTANCE,
            hIcon: LoadIconW(0 as HINSTANCE, IDI_APPLICATION),
            hCursor: LoadCursorW(0 as HINSTANCE, IDI_APPLICATION),
            hbrBackground: 16 as HBRUSH,
            lpszMenuName: 0 as LPCWSTR,
            lpszClassName: class_name.as_ptr(),
        };
    
        RegisterClassW(&wnd);
    
        let window_handle = CreateWindowExW(
            0,
            class_name.as_ptr(), 
            class_name.as_ptr(),
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            0, 0, 400, 400, 0 as HWND, 0 as HMENU,
            0 as HINSTANCE, std::ptr::null_mut());
    
        let mut msg = MSG {
            hwnd : 0 as HWND,
            message : 0 as UINT,
            wParam : 0 as WPARAM,
            lParam : 0 as LPARAM,
            time : 0 as DWORD,
            pt : POINT { x: 0, y: 0, }, 
        };
    
        ShowWindow(window_handle, SW_SHOW);
        UpdateWindow(window_handle);

        loop
        {
            let ret = GetMessageW(&mut msg, 0 as HWND, 0, 0);
            if ret == 0 {
                break;
            }
    
            if msg.message == WM_QUIT {
                break;
            }
    
            TranslateMessage(&mut msg);
            DispatchMessageW(&mut msg);
        }
    }
}

実行結果は、こんな感じです。

crate宣言不要等、初期よりは記述が簡易になりましたが、まだまだ面倒くさいですね。

まとめ

Windows 95以前の何となく懐かしい感じのするサンプルコードとなってしまいました。Windowsが培った資産とRustの長所が融合した、開発効率が向上するフレームワークの登場に期待します。また、マイクロソフトもWin32metadataという、RustからWindows APIを簡単に呼び出す仕組みを準備しているとのことなので、楽しみですね。

以上、RustでWindows APIを使いウィンドウを作成するの紹介でした。

コメント