Rust开发Windows API:如何将Box<Box<dyn Trait>>转为具体类型
Rust Windows API窗口:返回具体类型而非Trait的实现方案
核心思路
在保留通用窗口过程多态消息路由的同时,让窗口实例构造方法返回具体类型(如Box<ControlCenter>),而非Trait对象。关键是调整实例所有权与窗口过程指针传递逻辑,避免双层Box的类型不匹配问题,同时保证内存安全。
步骤1:调整AppWindow Trait与窗口过程
简化窗口过程中存储的指针类型,直接使用*mut dyn AppWindow,并添加WM_DESTROY消息的内存释放逻辑:
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { unsafe { match message { WM_CREATE => { let create_struct = lparam.0 as *const CREATESTRUCTA; // 从lpCreateParams取出dyn AppWindow的裸指针 let app_window = (*create_struct).lpCreateParams as *mut dyn AppWindow; // 存储到窗口额外数据 SetWindowLongPtrA(window, WINDOW_EXTRAS_MAIN, app_window as isize); // 调用实例的创建处理方法 (*app_window).handle_create((*create_struct).hInstance, window, message, wparam, lparam) } WM_DESTROY => { let app_window = GetWindowLongPtrA(window, WINDOW_EXTRAS_MAIN) as *mut dyn AppWindow; if !app_window.is_null() { // 将裸指针转回Box,自动释放内存 let _ = Box::from_raw(app_window); SetWindowLongPtrA(window, WINDOW_EXTRAS_MAIN, 0); } DefWindowProcA(window, message, wparam, lparam) } _ => { let app_window = GetWindowLongPtrA(window, WINDOW_EXTRAS_MAIN) as *mut dyn AppWindow; if !app_window.is_null() { (*app_window).process_message(window, message, wparam, lparam) } else { DefWindowProcA(window, message, wparam, lparam) } } } } } pub trait AppWindow { fn handle_create(&mut self, app_instance: HINSTANCE, window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT; fn process_message(&self, window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT; }
步骤2:重构ControlCenter的new方法
让new方法返回Box<ControlCenter>,同时将实例转换为*mut dyn AppWindow传递给窗口创建函数。添加Drop实现绑定实例与窗口生命周期:
#[derive(Default)] pub struct ControlCenter { hwnd: HWND, } impl AppWindow for ControlCenter { fn handle_create(&mut self, _app_instance: HINSTANCE, window: HWND, _message: u32, _wparam: WPARAM, _lparam: LPARAM) -> LRESULT { // 保存窗口句柄到实例中 self.hwnd = window; 0 // 返回0表示创建成功 } fn process_message(&self, window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { // 处理具体窗口消息,示例: match message { WM_PAINT => { // 绘制逻辑 DefWindowProcA(window, message, wparam, lparam) } _ => DefWindowProcA(window, message, wparam, lparam), } } } impl ControlCenter { pub fn new(instance: HINSTANCE) -> Box<ControlCenter> { let mut cc = Box::new(ControlCenter::default()); // 将具体实例转换为dyn AppWindow的裸指针 let app_window_ptr = cc.as_mut() as *mut dyn AppWindow; unsafe { CreateWindowExA( WINDOW_EX_STYLE::default(), WINDOW_CLASS_NAME, s!("Control Center"), WS_POPUP | WS_THICKFRAME | WS_VISIBLE, 0, 0, 300, 200, HWND::default(), None, instance, Some(app_window_ptr as *const c_void) ); } cc } pub fn show_window(&self) { unsafe { if !self.hwnd.is_null() { ShowWindow(self.hwnd, SW_SHOW); UpdateWindow(self.hwnd); } } } } // 添加Drop实现,确保实例销毁时销毁窗口 impl Drop for ControlCenter { fn drop(&mut self) { unsafe { if !self.hwnd.is_null() { DestroyWindow(self.hwnd); } } } }
步骤3:外部调用示例
现在可以直接返回具体类型并调用其专属方法:
let control_center = ControlCenter::new(app_instance); control_center.show_window(); // 当control_center离开作用域时,Drop会自动销毁窗口,窗口过程释放实例内存
关键说明
- 避免了双层Box的类型不匹配问题,直接将具体实例的指针转换为Trait对象指针传递给窗口过程。
- 通过
Droptrait绑定实例与窗口的生命周期,防止悬垂指针和内存泄漏。 - 通用窗口过程依然可以通过Trait对象实现多态消息路由,支持多种窗口类型复用同一个wndproc。
内容的提问来源于stack exchange,提问作者itachi
相关产品推荐
相关产品推荐

