学习博文https://blogs.msdn.microsoft.com/appconsult/tag/desktop-bridge/来了解UWP Desktop Bridge的Approach。

201610 Desktop Bridge – The bridge between desktop apps and the Universal Windows Platform

Desktop Bridge的前身叫做Project Centennial,可以把非WinRT程序(也就是Win32程序)打包成AppX格式,在Microsoft Store发行。

AppX是新的打包格式,InstallShield、Wix以及Advanced Installer都支持。从Win10 Anniversary Update开始,AppX可以直接点击安装。但是AppX需要一个可信的签名。 AppX的若干好处

  • 可以发布到Microsoft Store上
  • 可以通过脚本在计算机群组中自动分发
  • 支持Mobile Device Management工具,比如Microsoft Intune
  • 可以上传到网站上共享,或者通过USB直接拷贝

个人理解: AppX打包后的应用程序给操作系统提供了一个全新的管理粒度。Win32应用只有用户执行权限控制,应用本身没有权限控制。而AppX则引入了一些应用本身相关的权限控制。

用Desktop Bridge打包的好处:

  • 学习mac,整体打包,牺牲一点硬盘空间,换取操作性。易于安装卸载,整体性强。
  • 好多WinRT的API依赖于Package Identity,只有在打包的应用程序内才能使用。

如果将Win32程序转化为UWP程序,五步走:

  1. 转化(Convert):通过DAC(Desktop Application Converter)将Win32程序重新打包成AppX格式,使其具有Package Identity。
  • 注意:这一招现在已经不能用了。最新的办法是使用VS的Application Packaging Template工程来打包。
  1. 增强(Enhance):在Win32程序之内调用某些WinRT的API,来执行Toast通知,磁贴更新等等操作。
  2. 扩展(Extend):通过WinRT Component来扩展Win32程序,让其可以使用Background Task、App Services、XAML UI部件等等。
  3. 迁移(Migrate):打包两个程序,一个是UWP,另一个是Win32。以UWP为主,Win32作为扩展,以Desktop Extension的方式供UWP调用。
  4. 达成(Reach all):去除Win32部分,剩下完完全全的UWP程序。

通过Desktop Bridge打包的应用程序的执行权限依然很高,需要FullTrust,所以审批起来也会更严格。需要通过https://developer.microsoft.com/en-us/windows/projects/campaigns/desktop-bridge提交,然后等待AppConsult的工作人员联系你。

201610 Desktop Bridge – Enhancing a desktop application with the UWP APIs

本文展示了一个例子:https://github.com/qmatteoq/DesktopBridge/tree/master/3.%20Convert

上述例子在VS2019中无法构建

子啊WinForms中调用WinRT Api的话,需要在项目中引用Windows.winmd(C:\Program Files (x86)\Windows Kits\10\UnionMetadata)。完成这一步之后,就可以调用一部分WinRT的Api(https://msdn.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-supported-api)。

201611 Desktop Bridge – Identify the application’s context

在迁徙的过程中可以会遇到一些情况,你的代码既要可以当作Win32程序用,又要当作WinRT程序用。本文介绍了使用GetCurrentPackageFullName()来判断执行上下文,以便在代码中区别对待是Win32还是WinRT。

作者还提供了一个Helper库:https://github.com/qmatteoq/DesktopBridgeHelpers

201611 Desktop Bridge – Expanding a desktop application with a UWP component

UWP应用的生命周期和传统的Win32应用不同。Win32应用而言,用户有完全的决定权,可以决定何时启动应用,何时杀死应用,操作系统只是被动接受通知。对于UWP而言,为了节省资源和能耗,以适配更多的设备,不能让应用肆无忌惮运行并消耗资源。UWP有前台(foreground)和后台(background)的慨念。如果一个UWP应用切入后台,虽然这个应用程序还能驻留在内存,但是它具有的资源会被操作系统回收。

关于上面这一点,更多可以参考Windows team的博文:https://blogs.windows.com/buildingapps/2016/04/28/the-lifecycle-of-a-uwp-app/

UWP的后台,需要通过(幕后任务)Background Task这个慨念来执行。幕后任务跟触发事件挂钩,当一个幕后任务被触发时,能够执行一定的时间,比如最长30秒。

that Windows 10 Anniversary Update引入了一个概念,叫做single background process model,意味着实现幕后任务的代码不需要放置在一个Windows Runtime Component中,而是包含在UWP APP本身的代码中。

为什么传统的Win32应用程序有可能需要用到WinRT的幕后任务呢:

  • 幕后任务是事件驱动的,即使程序关闭,也可以被触发加载。而Win32程序必须保持本身一直运行(比如采用Windows Service的方式实现)。
  • 幕后任务不会跑飞。系统会监控幕后任务的资源使用。
  • 幕后任务的触发事件是系统提供的,Win32程序可能实现不了类似的触发器。

在本文的例子程序中,WinForms App需要引用:

  • The Windows.md file, stored in the path C:\Program Files (x86)\Windows Kits\10\UnionMetadata
  • The System.Windows.Runtime.dll file, stored in the path C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5.1

例子中的WinRT Component实现了一个幕后任务,并且需要在manifest中申明:

<Extensions>
  <Extension Category="windows.activatableClass.inProcessServer">
    <InProcessServer>
      <Path>CLRHost.dll</Path>
      <ActivatableClass ActivatableClassId="TileBackgroundTask.TileTask" ThreadingModel="both" />
    </InProcessServer>
  </Extension>
</Extensions>

上述申明在UWP应用中并不需要。

例子代码链接https://github.com/qmatteoq/DesktopBridge/tree/master/5.%20Extend,此项目尚需要使用xcopy将pri和winmd文件拷入到PackageLayout目录。

Matteo Pagani的一些Comments:

background tasks are handled by a unique Windows process (backgroundTaskHost.exe), there isn’t a separate process for every task. You can see all the registered tasks in Windows by opening the Windows Settings and go to Privacy -> Background Apps.

the best way would be to include an alias for your app in your converted version of the application: https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-extensions#alias

With this approach, an alias to your main executable will be registered into the system and, no matter where you are going to invoke it (a shortcut, a command prompt, another application, etc.), the converted version of your app will start.

Desktop Bridge – Multiple desktop processes, a single UWP container

你或许注意到了,一个UWP Container可以跑多个进程,它们共享本地存储(比如AppData)。上一篇笔记的例子中,一个UWP Container跑了两个进程,一个是Winforms应用的进程,另一个是幕后任务的进程。

处于同一个UWP Container的进程,它们的注册表都是虚拟,以及共享的。当AppX被删除时,相应注册表的内容也被清空。

本文例子: https://github.com/qmatteoq/DesktopBridge/tree/master/Extras/Container

201612 Desktop Bridge – The Migrate phase: invoking a Win32 process from a UWP app

本篇介绍了Desktop Bridge的Migrate阶段,并介绍了AppService。

从代码上看,AppService是一个WinRT Component,包含一个实现了IBackgroundTask接口的类,以及一个方法Run()来运行这个AppServie。和幕后任务不太相同的是AppService不需要指定Trigger,即可被其他App调用。(或者说,Trigger为其他App的调用)。

AppService的一些相关状况:

  • 使用到AppService的解决方案通常有两个工程,一个UWP APP工程,还有一个WinRT Component工程。
  • UWP App工程将WinRT Component注册成为AppService
  • 用户从Store安装UWP App的时候,也会顺带安装AppService

话锋一转,文章开始介绍separate process background model(分离进程后台模型)和 single process background model(单一进程后台模型)。后者在 Anniversary Update 中引入。

在分离模型中,幕后任务在backgroundTaskHost.exe中运行,这在共享资源时有所不便。在单一进程模型中,Run()方法被挪到了App中(比如App.xaml.cs),有了一个新的名字:OnBackgroundActivated。

接下来作者详细说明了例子:https://github.com/qmatteoq/DesktopBridge/tree/master/6.%20Migrate

其他

Full Trust Capability

xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"

<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
</Capabilities>

参考

(本篇完)