Metro应用程序运行在哪个桌面环境中?

9

作为WinAPI开发者,我很好奇Metro应用程序在哪个桌面中运行?

这些东西:

enter image description here


2
这是一个实现细节,可能会发生变化。你有什么实际的编程问题? - Raymond Chen
4
@RaymondChen:知识。除此之外,如果正在运行 Metro 应用程序,则可能需要显示弹出通知。 - c00000fd
3
已经有一个非常相似的问题了(https://dev59.com/5nrZa4cB1Zd3GeqP0UEb),但显然没有人知道答案。与其关注技术支持,我更想知道这样一个疯狂决定是如何被做出并实施并推出的,还有……忽略客户反馈。这太难以置信了,我真的很想知道其中的可能性。 - Cheers and hth. - Alf
@Cheersandhth.-Alf:RaymondChen可能知道,但他不会说,对吧? - c00000fd
2
这个话题很快就偏离了StackOverflow的主题。 - Raymond Chen
2
这是一个恶作剧问题,因为没有人使用Metro :) - Jonathan Potter
2个回答

26
我不知道这会是一个秘密...因此,我进行了一些调查,以下是我发现的内容:
首先回答我最初的问题--Metro(或现代UI)的东西在与“桌面”应用程序完全相同的desktop中运行(请原谅双关语)。实际上,这非常简单。简而言之,所有Microsoft批准的Metro东西都在Internet Explorer_Server容器(通俗来讲,就是Internet Explorer)或者在DirectUIHWND容器(这是Microsoft专有的类,用于呈现他们未记录的UI),全部在窗口中使用WS_EX_TOPMOST风格,这使它们渲染在其他内容之上。就是这样!
以下是一些示例:

让我们分割桌面并使用 Spy++ 查看底层发生了什么:

enter image description here

如果我们查看“天气”应用程序窗口,它只是类名为“Internet Explorer_Server”的常规(Win32)窗口,该窗口位于“Web Platform Embedding”类的窗口中,而后者又位于具有WS_EX_TOPMOSTWS_EX_NOREDIRECTIONBITMAP样式的“Windows.UI.Core.CoreWindow”容器中:

enter image description here

如果你深入了解,所有微软的Metro相关内容似乎都是从WWAHOST.exe进程运行的,简单来说,这是运行Metro应用程序JavaScript的容器。
现在让我们看看开始屏幕本身。由于它完全覆盖了桌面,我们需要使用不同的工具和其Shift键快照功能才能进入它:

enter image description here

从中我们可以获得开始屏幕的窗口句柄(在我的情况下为0x10158),并在Spy++中查找:

enter image description here

如你从这两个工具中所看到的,开始屏幕拥有窗口类DirectUIHWND,它位于一个名为ImmersiveLauncher类的窗口内部,该窗口拥有WS_EX_TOPMOSTWS_EX_NOREDIRECTIONBITMAP样式,使其保持在顶部。这是它和任何由“桌面”应用程序创建的其他窗口之间唯一的区别。
同样有趣的是,在分屏窗口情况下,“桌面”本身是如何呈现的。我最初认为在这种情况下,桌面只是被移动到一侧并调整大小,但实际上(或者说在我的Windows 8.1中)在桌面和Metro应用程序之间分屏的情况下,Metro应用程序仅简单地覆盖在桌面上,但桌面本身并没有改变其位置或大小。在这种情况下,只有任务栏和现有的桌面窗口被移动和调整大小以适应分屏。可以通过以下图示说明:

enter image description here

作为一个侧面的注释,对于用户来说,这种移动和调整大小可能会非常麻烦,因为当分割消失时,桌面窗口的原始位置和大小不会恢复。
最后,还有一个有些出乎意料的发现。我决定检查谷歌员工是如何实现他们的Chrome浏览器(作为Metro应用程序运行)并找到了这个:

enter image description here

Chrome在属于Google自己的进程"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"中呈现Windows.UI.Core.CoreWindow类窗口。因此,不需要深入了解,显然可以将Metro风格应用程序封装在非Microsoft容器中,这对于不关心AppStore XAML应用程序的开发人员来说是一个好消息 :)
编辑:忘记提到,如果您计划从可见于Metro应用程序顶部的Win32进程显示自己的弹出消息,则需要执行以下操作:
  • 在进程清单中设置UIAccess="true"。您可以在Visual Studio中通过转到项目属性->链接器->清单文件并将UAC Bypass UI Protection设置为YES来完成此操作。(请注意,您可以保持UAC Execution LevelasInvoker,或者不要求提升您的进程权限。)

  • 对您的进程进行代码签名。这很重要,因为没有签名它将无法工作,您将看到此错误消息:“从服务器返回了一个引用。

enter image description here

除了签名(或在开发系统上进行测试的情况下),您可以将以下注册表键设置为0。 (尽管我没有尝试过,但由于明显的安全问题,我不建议这样做!但如果没有代码签名证书,则似乎是另一种测试的方式。)

HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\ValidateAdminCodeSignatures
  • 将编译后的可执行文件放置在%windir%\System32文件夹中,或更现实的是放置在%ProgramFiles%\Company\Product文件夹中,或者放置在另一个%ProgramFiles(X86)%\Company\Product文件夹中以供产品安装使用。

另外,您可以考虑阅读雷蒙德·陈(Raymond Chen)的文章了解更多相关主题。

  • 之后,当您在弹出窗口上设置WS_EX_TOPMOST样式时,它将显示在任何其他窗口之上,包括Metro应用、开始屏幕等。

因此,换句话说,做这个:

//You may also consider setting the WS_EX_NOACTIVATE style
::SetWindowPos(hWnd, HWND_TOPMOST, 0, 0 , 0, 0, SWP_NOMOVE | SWP_NOSIZE);

能够实现这个:

enter image description here


7

你说你实际的问题是如何知道 Metro 应用是否正在运行,答案是调用 IAppVisibility::GetAppVisibilityOnMonitor。传入要检查的监视器。请注意这将给出正确的答案,无论应用程序在哪个桌面上运行。


嗯,谢谢。虽然我不太清楚如何从Win32应用程序中调用它? - c00000fd
2
正如文档中所述,调用CoCreateInstance函数并传入CLSID_AppVisibility。这将为您提供一个支持IAppVisiblity的对象。调用GetAppVisibilityOnMonitor方法即可。 - Raymond Chen
谢谢你,雷蒙德。我尝试在VS 2008中实现这个接口,但它没有定义:http://stackoverflow.com/q/23207291/843732 有什么办法可以在VS 2012之前做到这一点吗? - c00000fd
2
该界面在Windows 8中被引入,因此您需要安装Windows 8的平台SDK。 - Raymond Chen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接