突然想玩WinAPI。但是说实在的,真不想写C++。
正好Rust那边有人包好了一个crate:winapi方便我们调用,资料也有一些,抄点儿玩玩看。
环境
操作系统Win10 1909,rustc --version
为rustc 1.39.0 (4560ea788 2019-11-04)
,cargo --version
为cargo 1.39.0 (1c6ec66d5 2019-09-30)
,依然使用Visual Studio 2019的link.exe。
参考
- Rust で Windows プログラミング - CreateWindow編
- 一週間で身につくWIN32プログラミングの基本 第3日目:文字の表示
- recombinant/PetzoldPW5e-rs/Chapter 06 - The Keyboard/key_view1/src/main.rs
实践
Rust包作成
Set-Location D:\prj
cargo new hello_window
修改.\Cargo.toml
[package]
name = "hello_window"
version = "0.1.0"
authors = ["user"]
edition = "2018"
[dependencies.winapi]
version = "0.3"
features = ["winuser", "winbase"]
修改.\src\lib.rs
use std::mem;
use std::ptr;
use winapi::{
shared::{
minwindef::{LPARAM, LRESULT, UINT, WPARAM},
windef::{HBRUSH, HWND},
},
um::{
winbase::lstrlenW,
wingdi::{GetStockObject, TextOutW, WHITE_BRUSH},
winuser::{
BeginPaint, CreateWindowExW, DefWindowProcW, DispatchMessageW, EndPaint, GetMessageW,
LoadCursorW, LoadIconW, PostQuitMessage, RegisterClassW, ShowWindow, TranslateMessage,
UpdateWindow, CS_HREDRAW, CS_VREDRAW, IDC_ARROW, IDI_APPLICATION, MSG, PAINTSTRUCT,
SW_NORMAL, WM_DESTROY, WM_PAINT, WNDCLASSW, WS_OVERLAPPEDWINDOW,
},
},
};
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>();
let mut msg = mem::MaybeUninit::<MSG>::uninit().assume_init();
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!").as_ptr(),
WS_OVERLAPPEDWINDOW,
0,
0,
240, // window width
160, // window height
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_PAINT => {
let greetings = encode("Welcome!").as_ptr();
let mut ps = mem::MaybeUninit::<PAINTSTRUCT>::uninit().assume_init();
let hdc = BeginPaint(hwnd, &mut ps);
TextOutW(hdc, 5, 5, greetings, lstrlenW(greetings));
EndPaint(hwnd, &mut ps);
}
WM_DESTROY => PostQuitMessage(0),
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
};
0
}
解说
并没有WinAPI的经验,看看上面的参考就好吧。
稍微注意一下(实际已被注释掉):
let mut msg = mem::uninitialized::<MSG>();
编译器会抱怨它deprecated,那么就替换一下:
let mut msg = mem::MaybeUninit::<MSG>::uninit().assume_init();
再就是,想用lstrlenW
,就必须在.\Cargo.toml
把winbase
要到手,不然哪怕use语句写得好好的,代码提示或许也能看得见,但它就是干瞪眼,说找不到。
在这个项目,并不是说Rust编译得通就OK。哪怕类型看上去对应得上,编译通过,还是达不到预期效果。
虽然无关紧要:之前也玩过MessageBox,参见:Rust で Windows プログラミング - MessageBox編。并不复杂,照做就是。
后记
至于说当时为什么想玩WinAPI,其实是希望看看制作TSF框架输入法的可行性。现在看来,要是我的话,可能还不够吧。毕竟连个窗口都费尽心思。
行吧,就当自己赚到了点经验。