LoginSignup
19
12

More than 5 years have passed since last update.

Rust で Windows プログラミング - CreateWindow編

Last updated at Posted at 2019-02-03

環境

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"などは追加しなくても良いようです。

Cargo.toml
[dependencies.winapi]
version = "0.3"
features = ["winuser"] #"windef", "minwindef", "wingdi" は書かなくても OK

続いて main.rs に使用する module を書いていきます。MessageBox 編と比べるとかなり増えます。

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;

main 関数です。WNDCLASSを登録して、CreateWindowして、メッセージループを回すというおなじみ(?)の流れですね。

main.rs
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 で調べてみてください。)

main.rs
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関数です。

main.rs
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 のu32usizeisizeとも書けますが、WinAPI の慣習(?)に従って、型を明示した方が分かりやすいと思います。今回のサンプルではそんなに有難味が分からないですが、msgの条件分岐が多くなってくると Rsut のmatch式パワーが発揮されます。

main.rs
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 をまとめてどうぞ。

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編

19
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
12