スポンサーリンク

Rust+windows-rs,win32apiでCreateWindowしてみる

元のプログラムは以下。しかし一部コピペでは動かなかった部分もあるので修正をしている。

https://github.com/microsoft/windows-samples-rs/blob/master/create_window/src/main.rs

main.rs

以前作った自作WinMainを呼び出すmain関数。

main.rsと同じディレクトリに次のWinMain.rsが存在する。

use windows::Win32::System::LibraryLoader::GetModuleHandleA;
use std::env;
use windows::Win32::System::Threading::{GetStartupInfoW, STARTUPINFOW};

// mod ファイル名
mod winmain;
// use 関数名(など)
use winmain::WinMain;

// Rustのwindows-rsからウィンドウズアプリケーションをビルドする時には、
// WinWain ではなくmainをエントリポイントとする。

fn main()-> windows::core::Result<()>  {

    let hInstance:windows::Win32::Foundation::HINSTANCE;
    unsafe{
        // https://github.com/microsoft/windows-samples-rs/blob/master/create_window/src/main.rs
        hInstance = windows::Win32::System::LibraryLoader::GetModuleHandleA(None).unwrap();
    }

    // https://doc.rust-jp.rs/book-ja/ch12-01-accepting-command-line-arguments.html
    let args: Vec<String> = env::args().collect();


    // https://stackoverflow.com/questions/68322072/how-to-get-args-from-winmain-or-wwinmain-in-rust
    let mut si = windows::Win32::System::Threading::STARTUPINFOW {
        cb: std::mem::size_of::<windows::Win32::System::Threading::STARTUPINFOW>() as u32,
        ..Default::default()
    };
    unsafe { 
        windows::Win32::System::Threading::GetStartupInfoW(&mut si) 
    };
    let cmd_show = si.wShowWindow as i32;

    ////////////////////////////////////////////////////////////////////////////////////
    // 自作WinMainを呼び出す 
    WinMain(
        hInstance,
        windows::Win32::Foundation::HINSTANCE(0),
        args,
        cmd_show
    );


    Ok(())

}

WinMain.rs

use windows::{
    core::*, Win32::Foundation::*, Win32::Graphics::Gdi::ValidateRect,
    Win32::System::LibraryLoader::GetModuleHandleA, Win32::UI::WindowsAndMessaging::*,
};


extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
    unsafe {

        
        let rectnullptr: ::core::option::Option<*const windows::Win32::Foundation::RECT> = None;

        match message as u32 {
            WM_PAINT => {
                println!("WM_PAINT");
                ValidateRect(window, rectnullptr);
                LRESULT(0)
            }
            WM_DESTROY => {
                println!("WM_DESTROY");
                PostQuitMessage(0);
                LRESULT(0)
            }
            _ => DefWindowProcA(window, message, wparam, lparam),
        }
    }
}


/// 自作 WinMain
pub fn WinMain(
        hInstance : windows::Win32::Foundation::HINSTANCE, 
        hPrevInstance : windows::Win32::Foundation::HINSTANCE, 
        lpCmdLine:Vec<String>, 
        nCmdShow:i32
    )->i32{

    unsafe{
        let _hCursor = LoadCursorW(None,IDC_ARROW).unwrap();
        let _hInstance = hInstance;
        let _lpszClassName = PCSTR::from_raw("MY_NEW_WINDOW\0".as_ptr());
        let _style = CS_HREDRAW | CS_VREDRAW;

        // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/WindowsAndMessaging/struct.WNDCLASSA.html
        let wc=windows::Win32::UI::WindowsAndMessaging::WNDCLASSA{
            hCursor :_hCursor,
            hInstance: _hInstance,
            lpszClassName: _lpszClassName,
            style: _style,
            lpfnWndProc: Some(wndproc),
            ..Default::default()
        };

        let atom = RegisterClassA(&wc);
        debug_assert!(atom != 0);


        let nullptr: ::core::option::Option<*const ::core::ffi::c_void> = None;

        CreateWindowExA(
            Default::default(),
            _lpszClassName,
            PCSTR::from_raw("This is a sample window\0".as_ptr()),
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            None,
            None,
            _hInstance,
            nullptr,
        );


        let mut message = MSG::default();

        while GetMessageA(&mut message, HWND(0), 0, 0).into() {
            DispatchMessageA(&mut message);
        }
    }
    return 0;


}

Cargo.toml

 

[package]
name = "rs_win"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

[dependencies.windows]

version = "0.44.0"

# 最新版のバージョンは https://github.com/microsoft/windows-rs で確認
# ・バージョンによってfeaturesを変えなければいけないかもしれない

features = [
"Win32_Graphics_Gdi",
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging",
"Win32_System_LibraryLoader",
"Win32_System_Threading",
]

 

調べ方・記述方法など

関数や構造体の場所がわからないとき

例えば、WNDCLASSAが見つからずエラーになった場合、まず以下のURLへ飛ぶ。

https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/WindowsAndMessaging/struct.WNDCLASSA.html

Required features: "Win32_UI_WindowsAndMessaging", "Win32_Foundation", "Win32_Graphics_Gdi"

これらが必要なので、Cargo.tomlに記述する。

features = [
"Win32_Graphics_Gdi",
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging",
]

PCSTR型の文字列作成

PCSTR::from_rawを使う。

PCSTR::from_raw("This is a sample window\0".as_ptr()),

関数の戻り値がResult<>の場合

Result<>をunwrap()する。

let _hCursor = LoadCursorW(None,IDC_ARROW).unwrap();

NULLの作成

::core::option::OptionにNoneを代入

let nullptr: ::core::option::Option<*const ::core::ffi::c_void> = None;

問題はOptionのGeneric<>に何を渡すかで、これは関数定義を素直にコピペするのが一番楽。VSCode関数を右クリックなどして定義へ移動し、該当引数の型をそのまま持ってきてNone代入する。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: