在我的 Rust 实验中,我目前正在尝试以下操作。我想使用 windows crate 和 windows API 显示一个基本的空窗口。下面的代码编译并执行。它唯一不做的就是显示一个实际的窗口。我在这里做错了什么,因为代码看起来对我来说很重要,例如我相信 c++ 等效项会显示一个窗口。
use windows::{
core::*, Win32::{Foundation::*, Graphics::Gdi::{CreateSolidBrush, UpdateWindow, ValidateRect, COLOR_WINDOW, HBRUSH}, System::LibraryLoader::{GetModuleHandleA, GetModuleHandleW}, UI::WindowsAndMessaging::*},
};
fn main() -> Result<()> {
unsafe{
let window_class = w!("the_window");
let window_name = w!("The Factory");
let instance = GetModuleHandleW(None)?;
let kleur = COLORREF{
0: 255,
};
let brush: HBRUSH = CreateSolidBrush(kleur);
debug_assert!(instance.0 != 0);
//define the window
let wc = WNDCLASSW {
hCursor: LoadCursorW(None, IDC_ARROW)?,
style: CS_HREDRAW | CS_VREDRAW,
lpszClassName: window_name,
hInstance: instance.into(),
hbrBackground: brush,
lpfnWndProc: Some(wndproc),
..Default::default()
};
//register the class
let atom = RegisterClassW(&wc);
debug_assert!(atom != 0);
let hwnd_desktop = GetDesktopWindow();
let hwnd = CreateWindowExW(WINDOW_EX_STYLE::default(), window_class, window_name, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, hwnd_desktop, None, instance, None);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
let mut message = std::mem::zeroed();
println!("init the msg structure success.");
while GetMessageA(&mut message, None, 0, 0).into() {
TranslateMessage(&message);
DispatchMessageA(&message);
}
Ok(())
}
}
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
unsafe {
match message {
WM_PAINT => {
println!("WM_PAINT");
_ = ValidateRect(window, None);
LRESULT(0)
}
WM_DESTROY => {
println!("WM_DESTROY");
PostQuitMessage(0);
LRESULT(0)
}
_ => DefWindowProcA(window, message, wparam, lparam),
}
}
}
我无法追踪错误的根源,但我想知道
hwnd
参数是否为0或主消息循环返回-1。
还有一些备注:
WNDCLASS
和 CreateWindowEx
的 Unicode 变体(末尾均带有 W),但使用 DefWindowProc
、DispatchMessage
和 GetMessage
的 ANSI 变体。这会导致标题栏仅显示第一个字符。WINDOW_EX_STYLE::default()
,但如果它是无效标志,则可能会导致窗口没有正确的标题栏或首先显示。在 hWnd 是无效参数的情况下(例如引用已被销毁的窗口),可能返回 -1 值,这意味着此类代码可能会导致致命的应用程序错误。相反,使用这样的代码:
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
等效的 C++ 代码失败的原因与此代码相同:尝试创建尚未注册的类的窗口。
具体问题在这里:
let wc = WNDCLASSW {
// ...
lpszClassName: window_name,
// ...
..Default::default()
};
WNDCLASSW
结构,以名称window_
name
进行注册。随后对
CreateWindowExW()
的调用会请求创建 window_
class
类的窗口(尚未注册)。
在继续解决问题之前,让我们了解
CreateWindowExW()
可能会失败的事实,并添加以下错误处理代码:
let hwnd = CreateWindowExW(
// ...
);
if hwnd == HWND::default() {
return Err(Error::from_win32());
}
完成后,程序现在终止。发出
cargo run
会产生(除其他外)以下输出:
Error: Error { code: HRESULT(0x8007057F), message: "Cannot find window class." }
这很有帮助,并证实了注册的类名和请求的类名之间不匹配的怀疑。
修复方法很简单:确保注册的类名和请求的类名一致。将
WNDCLASSW
初始化更新为 read
let wc = WNDCLASSW {
// ...
lpszClassName: window_class,
// ...
..Default::default()
};
得到你
完成!
直到抓住右边缘并将其进一步向右移动。足以让人们毫无疑问地发现某些东西没有按预期显示。类似的东西
窗口标题应为
The Factory
,但显示屏显示为 T
。这是程序“G”阻止你关机的另一个化身。
简而言之:用于类注册的编码变体(
RegisterClassA
或RegisterClassW
)必须与所提供的窗口过程所假定的编码一致。最突出的例子是未处理消息的委托:
注册的类的窗口过程
RegisterClassA
必须委托给 DefWindowProcA
RegisterClassW
必须委托给 DefWindowProcW
相关代码使用
RegisterClassW
注册窗口类,但委托给 DefWindowProcA
,导致(以及其他问题)显示的窗口标题被截断。
这里的修复同样简单:
_ => DefWindowProcW(window, message, wparam, lparam),
产生这个窗口