最早的时候App Service被定义为一种后台服务,类似于极简版的Windows Service。App Service作为Background Task在宿主UWP APP中运行,向其他UWP APP提供服务,可用于UWP APP间通讯及交换数据。

早期的App Service应用场景较为单一,但随着Win10 1607版本对In Process AppService的支持,以及从Visual Studio2017开始支持的Desktop Extension和MSIX Package等一系列技术的应用,如今的App Service可以用于UWP和非UWP程序间的直接通讯,达到无限接近传统桌面程序的能力。我们今天就先来看一下In Process App Service。
In Process,顾名思义我们不需要额外创建专门的Project用来写App Service的代码。而是直接包含在主UWP工程。首先我们创建空的UWP工程FrontUWPApp,然后添加一个简单的帮助类AppServiceHandler:

class AppServiceHandler
{
private AppServiceConnection AppServiceConnection { get; set; }
private BackgroundTaskDeferral AppServiceDeferral { get; set; } public event EventHandler<string> MessageReceivedEvent; private static AppServiceHandler instance;
public static AppServiceHandler Instance
{
get
{
if (instance == null)
{
instance = new AppServiceHandler();
} return instance;
}
} private AppServiceHandler()
{ } public void BackgroundActivated(IBackgroundTaskInstance taskInstance)
{
AppServiceTriggerDetails appService = taskInstance.TriggerDetails as AppServiceTriggerDetails;
AppServiceDeferral = taskInstance.GetDeferral();
AppServiceConnection = appService.AppServiceConnection;
AppServiceConnection.RequestReceived += OnAppServiceRequestReceived;
AppServiceConnection.ServiceClosed += AppServiceConnection_ServiceClosed;
} private void OnAppServiceRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
AppServiceDeferral messageDeferral = args.GetDeferral();
var message = args.Request.Message;
string text = message["response"] as string; MessageReceivedEvent?.Invoke(this, text);
messageDeferral.Complete();
} private void AppServiceConnection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
{
AppServiceDeferral.Complete();
} public async Task<AppServiceResponse> SendRequestAsync(string message)
{
var valueSet = new ValueSet();
valueSet.Add("request", message);
return await AppServiceConnection.SendMessageAsync(valueSet);
}
}

这其中最重要的方法是

public void BackgroundActivated(IBackgroundTaskInstance taskInstance)

该方法将在App.xaml.cs通过

protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
base.OnBackgroundActivated(args);
AppServiceHandler.Instance.BackgroundActivated(args.TaskInstance);
}

将BackgroundTask的实例传递进来。再保存这个Instance中AppService的AppServiceConnection对象。在取得AppServiceConnection对象后,即可以通过事件

public event TypedEventHandler<AppServiceConnection, AppServiceRequestReceivedEventArgs> RequestReceived;

来监听消息,同时又可以通过方法

public IAsyncOperation<AppServiceResponse> SendMessageAsync(ValueSet message);

来发送消息。实现一个双向的通讯过程。
仅通过代码也许难以想象要做的事情,不妨由界面来推导出逻辑,下图是UWP工程FrontUWPApp的界面,我们希望发送文字消息给非UWP工程BackgroundNetProcess。再由BackgroundNetProcess处理消息后,主动经AppService推给FrontUWPApp。

首先我们在MainPage的OnNavigatedTo方法中通过desktop extension的方式,来启动.NET Framework的Console程序BackgroundNetProcess(如果对UWP如何使用desktop extension不够了解,请参考这篇《迁移桌面程序到MS Store(9)——APPX With Desktop Extension》)。同时给AppServiceHandler订阅MessageReceivedEvent。

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e); if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
AppServiceHandler.Instance.MessageReceivedEvent += Instance_MessageReceivedEvent;
}
}

Instance_MesssageReceivedEvent就是简单的把从BackgroundNetProcess中返回的消息显示在界面上。

        private async void Instance_MessageReceivedEvent(object sender, string e)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
textBoxResponses.Text += e + "\r\n";
});
}

同时MainPage上的Button按钮会通过AppServiceHandler实例中保存的AppServiceConnection对象来发送request给BackgroundNetProcess进程。

        private async void Button_Click(object sender, RoutedEventArgs e)
{
var response = await AppServiceHandler.Instance.SendRequestAsync(textBoxRequest.Text);
}

我们转到BackgroundNetProcess工程,在Main方法中仅仅是创建类BackgroundProcess的实例,并且让Console保持运行。

        static void Main(string[] args)
{
var backgroundProcess = new BackgroundProcess();
Console.ReadKey();
}

而在BackgroundProcess类中,我们通过InitializeAsync方法来创建AppServiceConnection对象,在成功打开Connection的情况下,订阅ReqeustReceived事件。这是为了能接受到上文提到的,UWP APP发送过来的request。

    public class BackgroundProcess
{
private AppServiceConnection Connection { get; set; } public Task InitializeTask { get; private set; } public BackgroundProcess()
{
InitializeTask = InitializeAsync();
} public async Task InitializeAsync()
{
Connection = new AppServiceConnection();
Connection.PackageFamilyName = Package.Current.Id.FamilyName;
Connection.AppServiceName = "NotificationAppService";
AppServiceConnectionStatus status = await Connection.OpenAsync();
if (status != AppServiceConnectionStatus.Success)
{
Console.WriteLine(status);
}
else
{
Console.WriteLine(status);
Connection.RequestReceived += Connection_RequestReceived;
}
} private async void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
var deferral = args.GetDeferral();
var content = args.Request.Message["request"];
var message = new ValueSet();
message.Add("response", $"Received request content: {content}");
await Connection.SendMessageAsync(message);
deferral.Complete();
}
}

这里需要注意的是,Connection.AppServiceName需要和最终Package.appmanifest文件中配置的ServiceName一致(appmanifest文件的修改我们后面一点再介绍)。

在BackgroundProcess类中,一旦我们收到了UWP APP发来的request,就会触发Connection_RequestReceived方法。在该方法里,我们对收到的字符串做了简单处理,然后通过SendMessageAsync方法反向给UWP APP发送消息。
当然,并没有规定收到request就一定要立即返回消息。我们可以在BackgroundProcess这样的desktop extension进程中,实现一些UWP限制的功能,诸如查询注册表,启动其他exe程序等等。甚至可以挂个键盘钩子,在捕捉到热键时,通知UWP APP。
前后端的FrontUWP和BackgroundNetProcess都介绍完了,接着就是通过Packaging工程将它们整合打包成MSIX package。

记得在Package工程的Applications中,添加对FrontUWPApp和BackgroundNetProcess的引用。同时设置FrontUWPApp为入口点。

最后我们来编辑Package工程的appxmanifest文件,主要就是添加Extensions节点。

      <Extensions>
<uap:Extension Category="windows.appService">
<uap:AppService Name="NotificationAppService" />
</uap:Extension>
<desktop:Extension Category="windows.fullTrustProcess" Executable="BackgroundNetProcess\BackgroundNetProcess.exe"></desktop:Extension>
</Extensions>

在完成以上操作之后,我们的AppServiceCommunicaton工程就编写完毕了。在Visual Studio 2019中按F5运行的话,应该可以实现FrontUWPApp和BackgroundNetProcess之间的消息传递了。
本篇的示例代码依然放在这个Repository中,Clone后通过VS打开,找到InProcessAppService文件夹即可。
https://github.com/manupstairs/UWPSamples

2020年的UWP(2)——In Process App Service的更多相关文章

  1. 2020年的UWP(3)——UWP和desktop extension的简单交互

    上一篇<2020年的UWP(2)--In Process App Service>中我们了解了UWP和Desktop Extension可以通过AppService进行数据交互.本篇我们就 ...

  2. 2020年的UWP(4)——UWP和等待Request的Desktop Extension

    上一篇我们讨论了UWP和Desktop Extension交互中,Desktop Extension执行后立即退出的场景.下图是提到的四种场景分类: 执行后立即退出 等待request,处理完后退出 ...

  3. UWP - 介绍App Service 与新功能

    App Service 是一种背景工作运行的服务,提供给其他Apps 使用就像Web Service.它本身无使用介面(UI-less),允许Apps 在同一个设备被引用,甚至Windows 10 1 ...

  4. UWP/Win10新特性系列—App Service

    Win10中,新增了一个很实用的新特性叫做App Service,App Service允许App不在前台运行的情况下提供出一个或多个对外服务供其他App使用,这看起来就好像Web开发中的Web Ap ...

  5. 在 UWP 应用中创建、使用、调试 App Service (应用服务)

    在 Windows 10 中微软为 UWP 引入了 App Service (即应用服务)这一新特性用以提供应用间交互功能.提供 App Service 的应用能够接收来自其它应用传入的参数进行处理后 ...

  6. 2020年的UWP——通过Radio类控制Cellular(1)

    最近在做UWP的项目,在2020年相信这已经是相对小众的技术了,但是在学习的过程中,发现某软这么几年仍然添加了不少的API,开放了相当多的权限.所以打算总结一下最近的一些经验和收获,介绍一下2020年 ...

  7. 为应用程序池“XX”提供服务的进程在与 Windows Process Activation Service 通信时出现严重错误

    场景 WCF应用程序部署在IIS7中,使用net.tcp协议对外给几百台客户端提供服务,应用程序池不断崩溃重启. 分析过程 在事件查看器中看到的错误信息类似于 为应用程序池“XX”提供服务的进程在与 ...

  8. Anroid 4大组件之android.app.Service

    android.app.Service A Service is an application component representing either an application's desir ...

  9. Fixed: The Windows Process Activation Service service terminated with the following error: The system cannot find the file specified

    I'm not yet clear what I did, but I'm blogging it so it can be found if someone else has this issue. ...

随机推荐

  1. mariadb 3

    MariaDB第三章(select)   基本查询 --查询基本使用(条件,排序,聚合函数,分组,分页) --创建学生表 create table students ( id int unsigned ...

  2. 关于弹性布局flex

    什么时候使用flex布局? 当页面排版涉及左右浮动.垂直居中等时,应使用flex布局来避免传统的盒式布局带来的一些Bug. 如何使用flex布局? 在目标元素的父元素设置csss属性.display: ...

  3. 修改python包pip下载国内源

    第一种方式- pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ 附国内常用镜像源:阿里云:https:// ...

  4. @Autowried入门和源码分析

    话不多说直接上代码: 声明一个接口userDao: package ioc.hello; public interface UserDao { public void test(); } 2个实现类: ...

  5. OpenCV计算机视觉学习(1)——图像基本操作(图像视频读取,ROI区域截取,常用cv函数解释)

    1,计算机眼中的图像 我们打开经典的 Lena图片,看看计算机是如何看待图片的: 我们点击图中的一个小格子,发现计算机会将其分为R,G,B三种通道.每个通道分别由一堆0~256之间的数字组成,那Ope ...

  6. 【JAVA】mysql数据库常见知识点

    目录 1.事务四大特性 2.数据库隔离等级 3.Mysql两种存储引擎的区别 4.哈希索引和B+树索引 5.聚簇索引和非聚簇索引 6.索引的优缺点,什么时候使用索引,什么时候不能使用索引 7.索引的底 ...

  7. st表、RMQ和LCA

    int lca(int x,int y) { if(de[x]<de[y]) swap(x,y); int d=de[x]-de[y]; for(int i=log2(d);i>=0;i- ...

  8. RabbitMQ与Kafka选型对比

    背景 本公司是.Net项目,在.Net可选的MQ比较少,主要Kafka和RabbitMQ,RabbitMQ我也是使用多年了,最近的Kafka广告与流行度打得使我也是无法无视,因此也是花了点时间收集了资 ...

  9. 文件上传限制条件(JS、后缀、文件名、类型、截断)绕过及修复建议

    在现代互联网的Web应用程序中,上传文件是一 种常见的功能,因为它有助于提高业务效率,比如企业的OA系统,允许用户上传图片.视频.头像和许多其他类型的文件.然而向用户提供的功能越多,Web应用受到攻击 ...

  10. Metasploit之令牌窃取

    令牌简介及原理 令牌(Token) 就是系统的临时密钥,相当于账户名和密码,用来决定是否允) 许这次请求和判断这次请求是属于哪一个用户的.它允许你在不提供密码或其他凭证的前提下,访问网络和系统资源.这 ...