rust - 如何使用 winrt-rs 创建 MessageDialog?

标签 rust windows-runtime

我试图在按键时显示 MessageDialog。到目前为止,它似乎没有做任何事情。代码可以编译,但无法运行。代码验证按键是否有效,但对话框根本不会显示。我尝试使用返回的 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与其主线程相关联。通常,对话框会查询此窗口并将其自身显示为模式。但是,在 Win32 应用程序中,系统无法假设您要使用哪个窗口。在这些情况下,您需要对 IInitializeWithWindow 进行 QI接口(interface)并使用您的窗口句柄调用初始化函数。

由于 IInitializeWithWindow 接口(interface)是纯 COM 接口(interface)而不是 WinRT 接口(interface),因此 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 窗口获取窗口句柄,您需要 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);

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

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

关于rust - 如何使用 winrt-rs 创建 MessageDialog?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62107050/

相关文章:

c# - 无法使用 c# 导航到 Windows Metro App 上的页面

c# - ComboBox 打开时 KeyDown 事件不起作用?

windows - AppContainer完整性级别

c# - 使用 CSWinRT 调用 .net5 windows api

rust - 在ioctl中应该使用哪个文件描述符来了解终端屏幕的大小?

generics - 取决于特征的通用实现

network-programming - 如何从 Rust 获取机器的 IP 地址列表?

rust - 预期的关闭,发现不同的关闭

rust - 如何打破 Rust 中的 do-while 样式循环?

c# - 如何找到包含等待/异步代码的间歇性失败单元测试的原因?