利用 Win32 启动和检测 UWP App 的方法
一種啟動和檢測 UWP 應用的方法
背景
我們發(fā)布過多款 UWP 平臺的同類型 App ,最近有一個需求:用傳統(tǒng) Win32 程序啟動我們的 UWP 程序。因為我們的每一個UWP App在客戶機器上都是互斥的,也就是同時只能存在一個,并且我們的win32程序也只有一個版本,所以啟動 UWP App 時,需要先檢測,再啟動。
我們大概有4個辦法,前3個比較扯,第4個目前可行,也是我們采用的。這4個方法的主要關注點是:如何檢測客戶機器上是否有我們的 UWP App。至于調(diào)用,方法比較簡單。
Solution 1
Win32 和 UWP 交互,首先想到的就是微軟的 Desktop Bridge 相關的內(nèi)容,找了一圈,倒是發(fā)現(xiàn)了 Win32 調(diào)用 UWP Api 的方法,不過可以調(diào)用的 Api 有限,而且文檔比較殘缺,最麻煩的就是要對 Win32 Project 配置修改,引入一堆 WinRT 的東西。嘗試了半天,終于不報錯了,但是運行時會奔潰,原因未知,有待繼續(xù)探索。而且比較存疑的是官方文檔有矛盾,我們用到的 Windows.System.Launcher Api 是否被這種調(diào)用方式支持不明確,因為報錯我們也無法驗證。
有興趣的小伙伴可以參考以下鏈接:
Desktop Bridge
Enhance your desktop application for Windows 10
UWP APIs available to a packaged desktop app (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"。這種方法真的很簡單粗暴,但是有幾個缺點:
Solution 3 (Solution 1和這個差不多)
微軟為我們提供了許多啟動 UWP 的方式,比如什么協(xié)議啟動,命令行啟動等,但是這些方法的使用前提是:我們的UWP app需要修改現(xiàn)有的 App Manifest,這對于已經(jīng)發(fā)布出去的UWP App,顯然是不可能的。(在我們的場景下,因為我們的 UWP App 和驅動綁定,一般隨驅動升級,比較穩(wěn)定,所以此方法不可用)
Solution 4 (Best solution)
隱約記得以前使用 Fiddler 的時候,有一個 WinConfig 功能,可以列出當前電腦上所有的 UWP 程序(實際上是 沙箱類程序,從 Windows 8 開始, UWP 也包含其中),然后可以進行 web 調(diào)試。所以就想能不能借鑒 Fiddler 的做法。然后理所應當?shù)陌l(fā)現(xiàn) Fiddler 安裝目錄下面有一個名為 EnableLoopback.exe 的程序,沒有為什么,我就把它丟到了ILSpy里面,完美的反編譯出了C#代碼,然后經(jīng)過一番探索,發(fā)現(xiàn)了AppContainer類,無論看類名還是類的定義,都很明確,這就是我們要找的東西,然后順著這個類看下去,找到了它獲取所有 UWP 程序的方法:通過 FirewallAPI.dll 里面的接口 NetworkIsolationEnumAppContainers 來枚舉。
有了了解,開始Coding!
BTW,如果想省事兒的話,直接把這個類相關的內(nèi)容導出,是可以直接用的。不過我們的 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);} }代碼很直白,里里面就兩個函數(shù),一個用來查找,一個用來啟動,額外用到的就是 Win32 Dll 調(diào)用相關的內(nèi)容了。
最后
可以看到,我們查找 UWP 比較麻煩,但是調(diào)用卻很簡單,核心就是:
"explorer.exe shell:AppsFolder\\{pfn}!App"很直白,赤裸裸的一個快捷方式呀!但是有坑,如果傳遞的參數(shù)有任何問題(要么拼錯了,要么不存在),explorer 會直接忽略參數(shù),把自己啟動。這種行為,對于不明真相的用戶,會很莫名其妙,垃圾軟件。所以我們在啟動我們的 UWP App 時,要確保這個我們的 App 一定存在于用戶的電腦上面,所以才有了上面檢測 UWP App 的邏輯。如果參數(shù)錯誤,explorer 啥也不敢的話,我們就不這么麻煩了,可以直接把我們所有的 UWP app 挨個啟動一遍,簡單粗暴!
最后的最后
我們用到了 Fillder 里面所使用的方法,但對于 Fiddler 版權的各種問題,個人不了解。好在我們直接用 C++ 實現(xiàn),沒有任何影響。 權當學習學習!
之前網(wǎng)上有 Fiddler 2.x版本的源碼,但不清楚這軟件是不是開源。
致敬 Fiddler !
總結
以上是生活随笔為你收集整理的利用 Win32 启动和检测 UWP App 的方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nagios 监控 mysql 读写比
- 下一篇: 算法题:找出整数数组中两个只出现一次的数