为什么 WebViewControlProcess.CreateWebViewControlAsync() 永远不会完成?

Why is WebViewControlProcess.CreateWebViewControlAsync() never completing?

我正在尝试编写一些使用 Windows.Web.UI.Interop.WebViewControl 的 Rust 代码(这是一个通用的 Windows 平台进程外包装器,专门设计用于 Win32 应用程序可以使用 EdgeHTML),它是所有编译,但在运行时无法正常工作。

相关代码归结为这样,使用了 winit、winapi 和 winrt crates:

use winit::os::windows::WindowExt;
use winit::{EventsLoop, WindowBuilder};

use winapi::winrt::roapi::{RoInitialize, RO_INIT_SINGLETHREADED};
use winapi::shared::winerror::S_OK;

use winrt::{RtDefaultConstructible, RtAsyncOperation};
use winrt::windows::foundation::Rect;
use winrt::windows::web::ui::interop::WebViewControlProcess;

fn main() {
    assert!(unsafe { RoInitialize(RO_INIT_SINGLETHREADED) } == S_OK);

    let mut events_loop = EventsLoop::new();
    let window = WindowBuilder::new()
        .build(&events_loop)
        .unwrap();

    WebViewControlProcess::new()
        .create_web_view_control_async(
            window.get_hwnd() as usize as i64,
            Rect {
                X: 0.0,
                Y: 0.0,
                Width: 800.0,
                Height: 600.0,
            },
        )
        .expect("Creation call failed")
        .blocking_get()
        .expect("Creation async task failed")
        .expect("Creation produced None");
}

WebViewControlProcess 实例化有效,CreateWebViewControlAsync 函数似乎确实关心它作为 host_window_handle 收到的值(传递 0,或从实际 HWND 值,它会抱怨)。然而 IAsyncOperation 坚定地停留在 AsyncStatus.Started (0),因此 blocking_get() 调用无限期挂起。

A full, runnable demonstration of the issue (with a bit more instrumentation).

我感觉 WebViewControlProcess 出了问题:它的 ProcessId 停留在 0,而且看起来没有生成任何子进程。 ProcessExited 事件 似乎 没有被触发(我在实例化后立即附加了一些东西,它是否有机会在此之前被触发?)。在这种情况下,调用 Terminate() 可能会失败,E_FAIL.

我是否错过了使用 Windows.Web.UI.Interop 的某种初始化?还是有其他原因导致它不起作用?

尝试用 winrt::init_apartment(); 初始化 WebViewProcessControl 并且它可能需要一个单线程单元(根据这个 )。

更多关注Microsoft Edge Developer Guide

Lastly, power users might notice the apppearance of the Desktop App Web Viewer (previously named Win32WebViewHost), an internal system app representing the Win32 WebView, in the following places:

● In the Windows 10 Action Center. The source of these notifications should be understood as from a WebView hosted from a Win32 app.

● In the device access settings UI (Settings->Privacy->Camera/Location/Microphone). Disabling any of these settings denies access from all WebViews hosted in Win32 apps.

事实证明问题与线程有关:winit crate 在不同的线程中执行其事件循环,而我没有意识到这一点;我曾错误地认为 winit 是一种无害的抽象,结果证明 完全 不是。

我在尝试最小化和移植一个已知功能的 C++ 示例时发现了这一点,这次是手动执行所有 Win32 API 调用而不是使用 winit,因此转换是正确的。我让它开始工作,并发现了这一点:

IAsyncOperation 在事件循环中实现,在 DispatchMessageW 调用的深处。 That 是调用 Completion 处理程序的时间。因此,要完成操作,您必须 运行 同一线程上的事件循环。 (另一个线程上的事件循环不执行任何操作。)否则,它会保持 Started 状态。

幸运的是,winit is already moving to a new event loop which operates in the same thread,Windows 实施已在几天前落地;当我迁移我的代码以使用 winit 的 eventloop-2.0 分支,并使用 Completed 处理程序而不是 blocking_get() 时,一切都开始工作了。

我将澄清 winrt crate 的 blocking_get() 调用,这通常是原型设计时显而易见的解决方案:在这种情况下你不能使用它,因为它会导致死锁,因为它会阻塞直到 IAsyncOperation 完成,但 IAsyncOperation 将不会完成,直到您在事件循环 (DispatchMessageW) 中处理消息,这永远不会发生,因为您阻塞了线程。