利用 Win32 启动和检测 UWP App 的方法
一种启动和检测 UWP 应用的方法
背景
我们发布过多款 UWP 平台的同类型 App ,最近有一个需求:用传统 Win32 程序启动我们的 UWP 程序。因为我们的每一个UWP App在客户机器上都是互斥的,也就是同时只能存在一个,并且我们的win32程序也只有一个版本,所以启动 UWP App 时,需要先检测,再启动。
我们大概有4个办法,前3个比较扯,第4个目前可行,也是我们采用的。这4个方法的主要关注点是:如何检测客户机器上是否有我们的 UWP App。至于调用,方法比较简单。
Solution 1
Win32 和 UWP 交互,首先想到的就是微软的 Desktop Bridge 相关的内容,找了一圈,倒是发现了 Win32 调用 UWP Api 的方法,不过可以调用的 Api 有限,而且文档比较残缺,最麻烦的就是要对 Win32 Project 配置修改,引入一堆 WinRT 的东西。尝试了半天,终于不报错了,但是运行时会奔溃,原因未知,有待继续探索。而且比较存疑的是官方文档有矛盾,我们用到的 Windows.System.Launcher Api 是否被这种调用方式支持不明确,因为报错我们也无法验证。
有兴趣的小伙伴可以参考以下链接:
Desktop Bridge
Detect UWP App
- UWP Quick Tip - Detect any installed app on Windows 10 这也是 Solution 1 的核心,一个小 trick
Solution 2
简单粗暴,直接检测 UWP 的安装目录。一般 UWP 的默认安装路径就是 "C:\Program Files\WindowsApps"。这种方法真的很简单粗暴,但是有几个缺点:
- 可能有强迫症用户修改了 UWP 的安装路径。这种情况下,需要自行去查注册表,当然注册表键值是什么就需要baidu了;
- 如果直接枚举 "C:\Program Files\WindowsApps"的子目录,会有权限问题(System),普通用户权限只能访问类似 "C:\Program Files\WindowsApps\microsoft.windowscommunicationsapps_17.9126.21695.0_x64__8wekyb3d8bbwe"的特定 UWP App 目录,这就需要我们提前确定要查找的 UWP App的 pfn (package family name,UWP App 的特定标识,全球唯一)和版本,但是版本因为经常变化,比较不好确定。
Solution 3 (Solution 1和这个差不多)
微软为我们提供了许多启动 UWP 的方式,比如什么协议启动,命令行启动等,但是这些方法的使用前提是:我们的UWP app需要修改现有的 App Manifest,这对于已经发布出去的UWP App,显然是不可能的。(在我们的场景下,因为我们的 UWP App 和驱动绑定,一般随驱动升级,比较稳定,所以此方法不可用)
Solution 4 (Best solution)
隐约记得以前使用 Fiddler 的时候,有一个 WinConfig 功能,可以列出当前电脑上所有的 UWP 程序(实际上是 沙箱类程序,从 Windows 8 开始, UWP 也包含其中),然后可以进行 web 调试。所以就想能不能借鉴 Fiddler 的做法。然后理所应当的发现 Fiddler 安装目录下面有一个名为 EnableLoopback.exe 的程序,没有为什么,我就把它丢到了ILSpy里面,完美的反编译出了C#代码,然后经过一番探索,发现了AppContainer类,无论看类名还是类的定义,都很明确,这就是我们要找的东西,然后顺着这个类看下去,找到了它获取所有 UWP 程序的方法:通过 FirewallAPI.dll 里面的接口 NetworkIsolationEnumAppContainers 来枚举。
有了了解,开始Coding!
BTW,如果想省事儿的话,直接把这个类相关的内容导出,是可以直接用的。不过我们的 Win32 是用C++写的,所以要稍稍转换一下。
C++代码如下:
#include <Netfw.h>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
namespace Launcher
{
typedef DWORD(*pNetworkIsolationEnumAppContainers)(
_In_ DWORD Flags,
_Out_ DWORD *pdwNumPublicAppCs,
_Out_ PINET_FIREWALL_APP_CONTAINER *ppPublicAppCs
);
typedef DWORD(*pNetworkIsolationFreeAppContainers)(
_In_ PINET_FIREWALL_APP_CONTAINER pPublicAppCs
);
void LaunchSpecifcApp(wstring *pfn)
{
TCHAR szCommandLine[1024];
wsprintf(szCommandLine, L"explorer.exe shell:AppsFolder\\%ws!App", (*pfn).c_str());
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = TRUE;
BOOL bRet = ::CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
}
void LaunchUWPApp()
{
vector<wstring> uwpApps;
uwpApps.push_back(L"microsoft.windowscommunicationsapps_8wekyb3d8bbwe");
HMODULE FirewallAPIModule;
FirewallAPIModule = (LoadLibrary(L"FirewallAPI.dll"));
auto EnumAppContainersProc = pNetworkIsolationEnumAppContainers(GetProcAddress(FirewallAPIModule, "NetworkIsolationEnumAppContainers"));
auto FreeAppContainersProc = pNetworkIsolationFreeAppContainers(GetProcAddress(FirewallAPIModule, "NetworkIsolationFreeAppContainers"));
DWORD pdwNumPublicAppCs = 0;
PINET_FIREWALL_APP_CONTAINER ppPublicAppCs = NULL;
HRESULT re = EnumAppContainersProc(0, &pdwNumPublicAppCs, &ppPublicAppCs);
for (int i = 0; i < pdwNumPublicAppCs; i++)
{
auto appContainer = ppPublicAppCs[i];
for (int j = 0; j < uwpApps.size(); j++)
{
auto app = uwpApps.at(j);
transform(app.begin(), app.end(), app.begin(), tolower);
if (app == appContainer.appContainerName)
{
//launch it;
auto temp = uwpApps.at(j);
LaunchSpecifcApp(&temp);
}
}
}
FreeAppContainersProc(ppPublicAppCs);
FreeLibrary(FirewallAPIModule);
vector<wstring>().swap(uwpApps);
}
}
代码很直白,里里面就两个函数,一个用来查找,一个用来启动,额外用到的就是 Win32 Dll 调用相关的内容了。
最后
可以看到,我们查找 UWP 比较麻烦,但是调用却很简单,核心就是:
"explorer.exe shell:AppsFolder\{pfn}!App"
{pfn} 整体用App的pfn代替
很直白,赤裸裸的一个快捷方式呀!但是有坑,如果传递的参数有任何问题(要么拼错了,要么不存在),explorer 会直接忽略参数,把自己启动。这种行为,对于不明真相的用户,会很莫名其妙,垃圾软件。所以我们在启动我们的 UWP App 时,要确保这个我们的 App 一定存在于用户的电脑上面,所以才有了上面检测 UWP App 的逻辑。如果参数错误,explorer 啥也不敢的话,我们就不这么麻烦了,可以直接把我们所有的 UWP app 挨个启动一遍,简单粗暴!
最后的最后
我们用到了 Fillder 里面所使用的方法,但对于 Fiddler 版权的各种问题,个人不了解。好在我们直接用 C++ 实现,没有任何影响。 权当学习学习!
之前网上有 Fiddler 2.x版本的源码,但不清楚这软件是不是开源。
致敬 Fiddler !
利用 Win32 启动和检测 UWP App 的方法的更多相关文章
- 利用BLEU进行机器翻译检测(Python-NLTK-BLEU评分方法)
双语评估替换分数(简称BLEU)是一种对生成语句进行评估的指标.完美匹配的得分为1.0,而完全不匹配则得分为0.0.这种评分标准是为了评估自动机器翻译系统的预测结果而开发的,具备了以下一些优点: 计算 ...
- 签署 Centennial Program Addendum,使用 Desktop Bridge 将 Win32 应用转制成 UWP
原文 签署 Centennial Program Addendum,使用 Desktop Bridge 将 Win32 应用转制成 UWP 能上架 Windows 应用商店的并不一定必须是 UWP 应 ...
- xamarin.forms uwp app部署到手机移动设备进行测试,真机调试(device portal方式部署)
最近学习xamarin.刚好 手上有一个lumia 930.所以试一试把uwp app部署到手机上,并真机调试一把. 目前环境: 1.开发pc电脑是win10,版本1607.加入了insider,所以 ...
- Xamarin.iOS - 利用Settings插件与EAIntroView制作App的欢迎界面
Xamarin.iOS - 利用Settings插件与EAIntroView制作App的欢迎界面 关于欢迎界面 很多App第一次启动都会有一个欢迎界面,欢迎界面往往决定这用户对App的第一映像,所以欢 ...
- UWP app HelloWorld 的创建
步骤 1:在 Visual Studio 中创建新项目 启动 Visual Studio 2015 RC.将出现 Visual Studio 2015 RC 起始页. (从现在开始,我们将 Visua ...
- 在Xbox和Hololens 上部署、调试UWP App
在Windows 10 Device 上,UWP App可以快速部署进行调试.PC(平板)和Phone就不用多说,网上的文章比较多.今天专门介绍一下怎么在Xbox One和HoloLens上部署调试U ...
- 利用mysqld_multi启动管理多实例
利用mysqld_multi启动管理多实例 官方管理多实例的一个脚本peer #将之前的目录清空 [root@mysql01 mysql]# tree /data/mysql/ /data/mysql ...
- 打包一个UWP APP
Before packaging your app Test your app. Before you package your app for store submission, make sure ...
- iOS利用Application Loader打包提交到App Store时遇到错误The filename 未命名.ipa in the package contains an invalid character(s). The valid characters are:A-Z ,a-z,0-9,dash,period,underscore,but the name cannot start w
iOS利用Application Loader打包提交到App Store时遇到错误: The filename 未命名.ipa in the package contains an invalid ...
随机推荐
- Docker学习笔记 - Docker的镜像
一个容器实际上是运行在宿主机上的一个进程. 只不过在启动这个进程之前进行了一些特殊处理,让这个容器进入了一个全新的虚拟环境,与宿主机的环境分开, 所以这个进程及其子进程认为自己运行在一个独立的世界里面 ...
- Trensient的使用介绍
1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过 ...
- 微信小程序配置WSS协议
配置的是nginx转发,前提是你已经安装了nginx的软件并已经正常打开网页,安装好SSL协议,能打开https网页 下面是配置: 需要的话可以根据需求修改 server { listen 80; s ...
- Android:认识R类、findViewById方法查找组件、@string查找字符、@color查找颜色、@drawable查找图片、@dimen某个组件尺寸定义、项目引入资源国际化
导入 之前都是断断续续的看了一些于如何使用android开发的文章.资料等,到目前位置很多基础的东西都不清楚,于是去学习了别人的课程,才了认识了R类.findViewById方法查找组件.项目引入资源 ...
- Struts(九):值栈(OGNL)
引言 在我们开发过程中,往往会使用一个对像传递到一个具体的action中,之后到跳转页面中访问对应对象的具体的参数. 比如:我们搭建一个struts2项目: 回顾下如何搭建strut2: 1.下载的s ...
- JDK1.8源码(七)——java.util.HashMap 类
本篇博客我们来介绍在 JDK1.8 中 HashMap 的源码实现,这也是最常用的一个集合.但是在介绍 HashMap 之前,我们先介绍什么是 Hash表. 1.哈希表 Hash表也称为散列表,也有直 ...
- 如何用webgl(three.js)搭建一个3D库房-第二课
闲话少叙,我们接着第一课继续讲(http://www.cnblogs.com/yeyunfei/p/7899613.html),很久没有做技术分享了.很多人问第二课有没有,我也是抽空写一下第二课. 第 ...
- 线程的同步控制(Synchronization)
临界区(Critical Sections) 摘要 临界区(Critical Section) 用来实现"排他性占有".适合范围时单一进程的各线程之间. 特点 一个局部对象,不是一 ...
- chrome浏览器再次打开黑屏一段时间
打开chrome设置 最下面-显示高级设置 再拉到最下面-使用硬件加速模式(把勾去掉)
- 玩一玩基于Token的 自定义身份认证+权限管理
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录.大概的流程是这样的: 客户端使用用户名跟密码请求登录 服务端收到请求,去验证用户名与密码 验证成功后,服务端会签发一个 Toke ...