为什么我关闭了一个AppWindow,却打不开一个新的AppWindow

When I close an AppWindow, why can't I open a new AppWindow

我在我的 UWP 应用程序中使用 AppWindow。大多数时候一切都很好,但偶尔当我关闭应用程序时 window 会出现一些奇怪的行为。

有时,应用程序 window 似乎无法正常关闭。我观察到的是 app window 似乎关闭了(app window 的内容不见了),但是 app window 的标题已经移到了 main window标题栏。发生这种情况后,如果我尝试使用 appWindow.TryShowAsync() 打开一个新应用程序 window,它会失败并出现异常 “找不到元素。(HRESULT 异常:0x80070490)”

我按照 Microsoft 文档中的说明执行应用 windows 清理,大多数情况下,我可以多次关闭一个应用 window 并打开一个新应用 window .

知道为什么它有时不起作用吗?

PS 在 documentation 中有一条来自 Microsoft 日期为 07/19/2019 的说明,上面写着, “AppWindow 目前处于预览阶段。这意味着您可以将使用 AppWindow 的应用程序提交到商店,但已知某些平台和框架组件无法与 AppWindow 一起使用。”现在我们已经进入 2020 年中期,AppWindow 是否仍处于“预览版”?

更新2020-07-15

按照 Faywang – MSFT 的建议,我创建了一个小型测试应用程序来尝试重现该问题。到目前为止,我无法通过测试应用程序重现该问题,但这里有一些包含更多数据的更新。

我进行了大量测试,确实发现与调试版本相比,我的应用程序的发布版本出现问题的频率更低。所以,我几乎准备放弃并将奇怪的行为归因于来自 space 的 alpha 粒子,并将 1 翻转为 0,几乎。 :)

我发现无需用户执行任何重要操作,我的应用程序就会出现问题。也就是说,如果我启动应用程序,调出应用程序 window,然后关闭应用程序 window,问题可能会发生(大约 10 – 20 次尝试后)。当我用我的测试应用程序执行相同的序列时,我还没有看到问题发生。

所以,我仍然没有答案,但我有两个新的理论。最新的理论是,也许我的主页及其关联的 Xaml 没有正确初始化(这就是应用程序 window 后来出现问题的原因)。我这么说是因为在我的真实应用程序中,我在 应用程序 window 出现

之前 看到了这些消息

onecoreuap\windows\wgi\winrt\display\displaycommon.cpp(411)\Windows.Graphics.dll!00007FFCE38B04B0: (caller: 00007FFCE38B01F5) ReturnHr(1) tid(6358) 80070490 Element not found.

onecoreuap\windows\wgi\winrt\display\displaycommon.cpp(411)\Windows.Graphics.dll!00007FFCE38B04B0: (caller: 00007FFCE38B01F5) ReturnHr(2) tid(6358) 80070490 Element not found.

onecoreuap\windows\wgi\winrt\display\displaycommon.cpp(411)\Windows.Graphics.dll!00007FFCE38B04B0: (caller: 00007FFCE38B017A) ReturnHr(3) tid(6358) 80070490 Element not found.

onecore\com\combase\objact\dllcache.cxx(4713)\combase.dll!00007FFCEB90964E: (caller: 00007FFCEB811D1D) ReturnHr(1) tid(6358) 80040111 ClassFactory cannot supply requested class

当我在主页上显示图像时出现第一条消息。当我使用弹出菜单调用应用程序时会出现接下来的三个 window。当我在我的测试应用程序中调用应用程序 window 时,我没有看到这些消息。我一直忽略这些消息,因为主页似乎工作正常,弹出菜单工作正常,但可能已经造成了一些损坏。当我在网上搜索这些消息时,它们似乎很漂亮 non-specific 并且与许多其他与 App 无关的故障有关 Windows.

有没有人从这些消息中得到启发?

另一个理论是这个主要 window / app window 行为与我的代码无关,但是 Visual Studio 2019 抓取不同 Windows dll(如 Windows.Graphics.dll)以使用我的代码构建。真正的应用程序在过去的几个月里已经构建和重建,并且使用不同版本的 VS2019,但是测试应用程序是昨天才构建的。我刚刚更新到新版本的 VS2019,16.6.4 我一直在想,如果我删除 bin 和 obj 文件夹,然后用最新版本的 VS 重建我的应用程序,那么构建时不会有任何残留影响早期版本。

这是正确的想法吗?

还有一件事。我发现当我关闭应用程序 window 并且在关闭时出现奇怪的行为时,事件 ApplicationView.Consolidated 已经触发,并且 IsAppInitiated 和 IsUserInitiated 都是假的。该事件“在 window 从最近使用的应用程序列表中删除时发生,或者如果用户对其执行关闭手势。”

这是否意味着 Windows 认为用户正在尝试关闭主 window 而实际上用户正试图关闭应用程序 window?

更新#2 2020-07-15

按照 Ivan Verges 的建议,我正在发布一些代码。我将此作为带有一点一厢情愿的答案发布。也许有人会看到这里有什么问题,然后我会把它编辑成一个答案。 :)

你会看到我的代码大部分是从微软关于“Show multiple views with AppWindow”的例子中复制过来的。

Ivan 还想知道我是否有服务、后台任务或其他长生命周期数据 objects。对于服务或后台任务,答案是否定的。至于其他长寿命数据 objects,我不会将它们保存在我的 EditPage class 中(用于应用程序 window)。我在 MainPage class 中保留了 long lifetime objects(主要用于 window),并且只在编辑页面的控件中显示它们。 这是我创建应用程序 window 的代码(我在我的应用程序中将其称为编辑 window)。而得到的代码window 关闭时调用(由用户)

   // helper fcn launches the Edit window
    private async Task ShowEditAppWindow()
    {
        if (AppWindows.Count > 0)
        {
            MainFlyout.Hide();
            return;
        }

    // Create a new window.
    AppWindow appWindow = await AppWindow.TryCreateAsync();

    // Create a Frame and navigate the Frame to a XAML-style Page object
    Frame appWindowContentFrame = new Frame();
    appWindowContentFrame.Navigate(typeof(EditPage));

    // Attach the XAML content to the window
    ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);

    // Add the new page to the Dictionary using the UIContext as the Key.
    AppWindows.Add(appWindowContentFrame.UIContext, appWindow);
    appWindow.Title = "Edit Window ";

    // When the window is closed, be sure to release
    // XAML resources and the reference to the window. 
    appWindow.Closed += (ss, ee) => AppWindow_Closed(ss, ee, appWindowContentFrame);

    // try to show the Edit window
    InEditState = false;
    bool successShowing = false;

    try
    {
        successShowing = await appWindow.TryShowAsync();
    }
    catch (Exception ex)
    {
        successShowing = false;
        Debug.WriteLine("Failure showing Edit window. " + ex.Message);
    }
              
    if (successShowing)
    {
        InEditState = true;
    }
    else
    {
        // clean up attempted window
        MainPage.AppWindows.Remove(appWindowContentFrame.UIContext);
        appWindowContentFrame.Content = null;
        appWindow = null;
        InEditState = false;            }

}

   private void AppWindow_Closed(AppWindow sender, AppWindowClosedEventArgs args, 
        Frame appWindowContentFrame, AppWindow appWindow)
    {
        Debug.WriteLine("in AppWindow_Closed handler");
        MainPage.AppWindows.Remove(appWindowContentFrame.UIContext);
        appWindowContentFrame.Content = null;
        appWindow = null;
        InEditState = false;

    // set a variable to indicate the appWindow just closed
    EditWindowJustClosed = true;

    // start timer to clear that indication
    EditWindowJustClosedTimer.Start();

    // always get show going after edit window closes
    SlideTimer_Tick(null, null);        // simulate timeout of the Slide timer
}

我对这个问题没有完美的答案,但我现在有一个很好的答案。我发现了一些东西,学到了一些东西,减少了问题的发生(已经很低了)。

困扰我的一件事是我的应用程序中出现了我在简单测试应用程序中没有看到的错误消息。我确实找到了解释。这是 Visual Studio 中的项目设置。也就是说,用于测试应用程序的设置包括 Debug Tab |调试器类型 = 仅限托管。我用于真实应用程序的设置包括 Debug Tab |调试器类型 = 混合(托管和本机)。我依稀记得在某个时候更改过它,大概是一个月前,当时我正在寻找其他东西。貌似忘记改回来了

我的所有代码都是托管代码,所以我很高兴地看到,一旦我使用了“仅限托管”设置,关于 Windows dll 的消息就从输出 Window 中消失了,并使它就像测试应用程序。但是,该解释排除了我提出的用于解释应用 window 问题的两个理论。

Stefan 有了新的理论。他说有一个经验法则可以使 OnClosed 处理程序中的代码保持最少。我听说过在关闭过程 期间调用 的处理程序中保持代码最少,例如 OnClosing 处理程序,但显然您也想保持 OnClode 处理程序的执行时间短。谢谢斯蒂芬!

因此,我减少了在 OnClosed 处理程序中执行的代码量,这很有帮助。我仍然可以看到问题发生,但现在很难重现。

目前,这是我的解决方案。

谢谢大家!