为什么WebViewControlProcess.CreateWebViewControlAsync()从未完成?

时间:2019-02-01 04:30:43

标签: winapi rust windows-runtime interop

我正在尝试编写一些使用Windows.Web.UI.Interop.WebViewControl(这是通用Windows平台进程外包装程序,明确设计为Win32应用程序可以使用EdgeHTML)的Rust代码,并且都可以编译,但不能正常工作在运行时正确运行。

使用winit,winapi和winrt包装箱,相关代码可以归结为:

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,或与实际{{1} }的值,并且会抱怨)。然而HWND坚定地停留在IAsyncOperation(0)处,因此AsyncStatus.Started调用会无限期挂起。

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

我感觉到blocking_get()有错误:它的WebViewControlProcess停留在0,看起来没有产生任何子进程。 ProcessId事件不会似乎被触发(实例化后我立即向它附加了一些东西,在此之前是否有机会被触发?)。在这种情况下,ProcessExited调用Terminate()失败。

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

2 个答案:

答案 0 :(得分:4)

事实证明,问题出在与线程有关的问题上:winit板条箱正在另一个线程中进行事件循环,而我没有意识到这一点;我错误地认为winit是一种无害的抽象,事实证明并非如此。

当我尝试最小化和移植一个已知功能的C ++示例时,我发现了这一点,这次是手动进行所有Win32 API调用,而不是使用winit,因此翻译是正确的。我使它起作用,并发现了这一点:

IAsyncOperation在事件循环中完成,位于DispatchMessageW调用的深处。 是调用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)中处理消息为止,这种情况永远不会发生,因为您正在阻塞线程。

答案 1 :(得分:0)

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

更多关注Microsoft Edge Developer Guide

  

最后,高级用户可能会注意到Desktop App的出现   Web Viewer(以前称为Win32WebViewHost),一个内部系统应用程序   在以下位置表示Win32 WebView:

     

●在Windows 10操作中心中。这些通知的来源   应该理解为来自Win32应用程序托管的WebView。

     

●在设备访问设置界面中   (设置->隐私->摄像机/位置/麦克风)。禁用任何   这些设置拒绝访问Win32应用程序中托管的所有WebView。