如何使用 winrt-rs 创建 MessageDialog?

How can I create a MessageDialog using winrt-rs?

我试图在按键时显示一个消息对话框。到目前为止,它似乎没有做任何事情。该代码确实可以编译,但无法运行。代码验证按键是否有效,但对话框根本不会显示。我尝试使用返回的 IAsyncOperation 并使用 'get()' 但这似乎完全冻结了应用程序。我错过了什么?

//#![windows_subsystem = "windows"]
use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::WindowBuilder,
};

use winrt::*;
import!(
    dependencies
        os
    modules
        "windows.data.xml.dom"
        "windows.foundation"
        "windows.ui"
        "windows.ui.popups"
);

fn main() {
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new().build(&event_loop).unwrap();
    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;
        match event {
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                window_id,
            } if window_id == window.id() => *control_flow = ControlFlow::Exit,
            Event::WindowEvent {
                event: WindowEvent::Resized (_size),
                ..
            } => (),
            Event::WindowEvent {
                event: WindowEvent::KeyboardInput {input,..},
                ..
            } if input.state == winit::event::ElementState::Pressed => {
                use windows::ui::popups::MessageDialog;
                let mymsg = MessageDialog::create("Test").unwrap().show_async();
                println!("KeyState-{}",input.scancode);
            },
            _ => (),
        }
    });
}

这突出了在 Win32 应用程序中使用某些 WinRT API 的差异之一。在 UWP 应用程序中,您的应用程序有一个与其主线程关联的 CoreWindow。通常,对话框会查询此 window 并将其自身显示为模态。但是,在 Win32 应用程序中,系统无法假设 window 您想要使用什么。在这些情况下,您需要对 IInitializeWithWindow 接口进行 QI,并使用 window 句柄调用初始化函数。

由于 IInitializeWithWindow 接口是纯 COM 接口而不是 WinRT 接口,因此 winrt-rs 没有它的投影。相反,您需要自己定义它(确保您获得正确的 GUID!):

#[repr(C)]
pub struct abi_IInitializeWithWindow {
    __base: [usize; 3],
    initialize: extern "system" fn(
        winrt::NonNullRawComPtr<InitializeWithWindowInterop>,
        *mut c_void,
    ) -> winrt::ErrorCode,
}

unsafe impl winrt::ComInterface for InitializeWithWindowInterop {
    type VTable = abi_IInitializeWithWindow;

    fn iid() -> winrt::Guid {
        winrt::Guid::from_values(1047057597, 28981, 19728, [128, 24, 159, 182, 217, 243, 63, 161])
    }
}

#[repr(transparent)]
#[derive(Default, Clone)]
pub struct InitializeWithWindowInterop {
    ptr: winrt::ComPtr<InitializeWithWindowInterop>,
}

impl InitializeWithWindowInterop {
    pub fn initialize(
        &self,
        window: *mut c_void,
    ) -> winrt::Result<()> {
        match self.ptr.abi() {
            None => panic!("The `this` pointer was null when calling method"),
            Some(this) => unsafe {
                (this.vtable().initialize)(
                    this,
                    window,
                )
                .ok()?;
                Ok(())
            },
        }
    }
}

要从您的 winit Window 获取 window 句柄,您需要 raw-window-handle 箱子。从那里你可以为任何实现 HasRawWindowHandle:

的东西创建一个辅助特征
trait InitializeWithWindow {
    fn initialize_with_window<O: RuntimeType + ComInterface>(&self, object: &O) -> winrt::Result<()>;
}

impl<T> InitializeWithWindow for T
where
    T: HasRawWindowHandle,
{
    fn initialize_with_window<O: RuntimeType + ComInterface>(
        &self,
        object: &O,
    ) -> winrt::Result<()> {
        // Get the window handle
        let window_handle = self.raw_window_handle();
        let window_handle = match window_handle {
            raw_window_handle::RawWindowHandle::Windows(window_handle) => window_handle.hwnd,
            _ => panic!("Unsupported platform!"),
        };

        let init: InitializeWithWindowInterop = object.try_into()?;
        init.initialize(window_handle)?;
        Ok(())
    }
}

现在在您的事件循环中,您可以使用以下方式调用它:

let dialog = MessageDialog::create("Test").unwrap();
window.initialize_with_window(&dialog).unwrap();
dialog.show_async().unwrap();
println!("KeyState-{}",input.scancode);

请注意,我不是在等待 show_async 返回的 IAsyncOperation 的结果。原因是因为投影现在只支持同步等待,这会占用您的消息泵并导致 window 挂起。这意味着打印语句将 运行 在对话框 returns 之前。一旦更广泛的异步支持启动并且 运行ning 在投影中,这应该会有所改善。

您现在可以在对话框和选取器(例如 FileSavePicker、GraphicsCapturePicker)上使用 initialize_with_window 方法。