##環境
toolchain: stable-i686-pc-windows-msvc
rustc 1.32.0 (9fda7c223 2019-01-16)
winapi 0.3.6
##初めてのウィンドウ
MessageBox が表示されるだけでは物足りないのでウィンドウを作ってみましょう。
Cargo.toml の features にちょっと追加。
2019/2/12 追記
"windef", "minwindef", "wingdi"
などは追加しなくても良いようです。
[dependencies.winapi]
version = "0.3"
features = ["winuser"] #"windef", "minwindef", "wingdi" は書かなくても OK
続いて main.rs に使用する module を書いていきます。MessageBox 編と比べるとかなり増えます。
use winapi::{
um::{
winuser::{RegisterClassW, WNDCLASSW, CS_HREDRAW, CS_VREDRAW,
LoadIconW, IDI_APPLICATION, LoadCursorW, IDC_ARROW,
CreateWindowExW, ShowWindow, SW_NORMAL, UpdateWindow,
GetMessageW, TranslateMessage, DispatchMessageW, MSG,
WM_DESTROY, PostQuitMessage, DefWindowProcW, WS_OVERLAPPEDWINDOW},
wingdi::{GetStockObject, WHITE_BRUSH},
},
shared::{
windef::{HWND, HBRUSH},
minwindef::{UINT, WPARAM, LPARAM, LRESULT},
},
};
use std::ptr;
use std::mem;
main 関数です。WNDCLASS
を登録して、CreateWindow
して、メッセージループを回すというおなじみ(?)の流れですね。
fn main() {
unsafe {
let class_name = encode("my_window_class_name");
if !register_wndclass(&class_name) {
return;
}
let hwnd = create_window(&class_name);
if hwnd.is_null() {
return;
}
ShowWindow(hwnd, SW_NORMAL);
UpdateWindow(hwnd);
let mut msg = mem::uninitialized::<MSG>();
loop {
if GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
return;
}
TranslateMessage(&mut msg);
DispatchMessageW(&mut msg);
}
}
}
encode
関数は前回と同じなので省略します。では、register_wndclass
関数を見てみましょう。WNDCLASSW
構造体の初期化にはstd::mem::zeroed()
を使っています。構造体のメンバをすべて書き連ねるのは面倒なので、zeroed
で初期化して必要なメンバだけに値を設定すれば良いです。(ここでは WinAPI の詳細については述べないので、MSDN で調べてみてください。)
unsafe fn register_wndclass(class_name: &[u16]) -> bool {
let mut winc = mem::zeroed::<WNDCLASSW>();
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = Some(win_proc);
winc.hIcon = LoadIconW(ptr::null_mut(), IDI_APPLICATION);
winc.hCursor = LoadCursorW(ptr::null_mut(), IDC_ARROW);
winc.hbrBackground = GetStockObject(WHITE_BRUSH as i32) as HBRUSH;
winc.lpszClassName = class_name.as_ptr();
RegisterClassW(&winc) > 0
}
続いて、create_window
関数です。
unsafe fn create_window(class_name: &[u16]) -> HWND {
CreateWindowExW(
0, //dwExStyle: DWORD
class_name.as_ptr(), //lpClassName: LPCWSTR
encode("Hello, World!").as_ptr(), //lpWindowName: LPCWSTR
WS_OVERLAPPEDWINDOW, //dwStyle: DWORD
0, 0, 200, 200, //X: int, Y: int, nWidth: int, nHeight: int
ptr::null_mut(), //hWndParent: HWND
ptr::null_mut(), //hMenu: HMENU
ptr::null_mut(), //hInstance: HINSTANCE
ptr::null_mut(), //lpParam: LPVOID
)
}
最後にウィンドウプロシージャのwin_proc
関数です。引数のUINT
型、WPARAM
型や、戻り値のLRESULT
型などは、それぞれ Rust のu32
、usize
、isize
とも書けますが、WinAPI の慣習(?)に従って、型を明示した方が分かりやすいと思います。今回のサンプルではそんなに有難味が分からないですが、msg
の条件分岐が多くなってくると Rsut のmatch
式パワーが発揮されます。
unsafe extern "system" fn win_proc(hwnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
match msg {
WM_DESTROY => PostQuitMessage(0),
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
};
0
}
main.rs をまとめてどうぞ。
use winapi::{
um::{
winuser::{RegisterClassW, WNDCLASSW, CS_HREDRAW, CS_VREDRAW,
LoadIconW, IDI_APPLICATION, LoadCursorW, IDC_ARROW,
CreateWindowExW, ShowWindow, SW_NORMAL, UpdateWindow,
GetMessageW, TranslateMessage, DispatchMessageW, MSG,
WM_DESTROY, PostQuitMessage, DefWindowProcW, WS_OVERLAPPEDWINDOW},
wingdi::{GetStockObject, WHITE_BRUSH},
},
shared::{
windef::{HWND, HBRUSH},
minwindef::{UINT, WPARAM, LPARAM, LRESULT},
},
};
use std::ptr;
use std::mem;
fn main() {
unsafe {
let class_name = encode("my_window_class_name");
if !register_wndclass(&class_name) {
return;
}
let hwnd = create_window(&class_name);
if hwnd.is_null() {
return;
}
ShowWindow(hwnd, SW_NORMAL);
UpdateWindow(hwnd);
let mut msg = mem::uninitialized::<MSG>();
loop {
if GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
return;
}
TranslateMessage(&mut msg);
DispatchMessageW(&mut msg);
}
}
}
fn encode(source: &str) -> Vec<u16> {
source.encode_utf16().chain(Some(0)).collect()
}
unsafe fn register_wndclass(class_name: &[u16]) -> bool {
let mut winc = mem::zeroed::<WNDCLASSW>();
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = Some(win_proc);
winc.hIcon = LoadIconW(ptr::null_mut(), IDI_APPLICATION);
winc.hCursor = LoadCursorW(ptr::null_mut(), IDC_ARROW);
winc.hbrBackground = GetStockObject(WHITE_BRUSH as i32) as HBRUSH;
winc.lpszClassName = class_name.as_ptr();
RegisterClassW(&winc) > 0
}
unsafe fn create_window(class_name: &[u16]) -> HWND {
CreateWindowExW(
0,
class_name.as_ptr(),
encode("Hello, World!").as_ptr(),
WS_OVERLAPPEDWINDOW,
0, 0, 200, 200,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
}
unsafe extern "system" fn win_proc(hwnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
match msg {
WM_DESTROY => PostQuitMessage(0),
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
};
0
}
次回はウィンドウを最小化するとタスクトレイに格納されるようにします。
Rust で Windows プログラミング - Shell_NotifyIcon編