今回は、近年話題の言語Rustを用いて、Windowsのウィンドウを作成してみたいと思います。ゆくゆくは、Rustでガンガンロジック部分を書いて、高速・効率的なプログラムを目指したいところです。
Rustとは
下記の特徴を持つ、2015年に1stバージョンがリリースされた、比較的新しい言語です。
- C言語並みに低レイヤを扱える
- CPUやメモリの効率が良い
- 所有権の概念や強力な型チェックの導入、並列処理を安全に実装可能
- イテレータ・クロージャなど最新の表現力を保有
有名な製品プロジェクトでも、C言語からRustへの乗り換えを耳にすることが多くなってきました。
環境作成
Windows 10上に、Rustの開発環境を作成します。
依存モジュール
WindowsでRustを利用する場合、下記どちらかのモジュールをインストールしておく必要があります。
- Microsoft C++ Build Tools
- VisualStudioの「C++によるデスクトップ開発」ワークロード
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-rsとMSDNを参考にしながら、下記の通り記述しました。ウィンドウを作成し、画面に文字列を表示して、×ボタンか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を使いウィンドウを作成するの紹介でした。
コメント