How to use Win32 to launch and detect UWP App

A way to launch and detect UWP apps

background

We have released several apps of the same type on the UWP platform, and recently there is a requirement: to use traditional Win32 programs to start our UWP programs. Because each of our UWP apps are mutually exclusive on the client machine, that is, only one can exist at the same time, and our win32 program has only one version, so when starting the UWP App, it needs to be detected first, and then started.

We have about 4 methods, the first 3 are rather ridiculous, and the fourth is currently feasible, which is what we have adopted. The main focus of these 4 methods is: how to detect if our UWP App is present on the client machine. As for the call, the method is relatively simple.

Solution 1

When Win32 interacts with UWP, the first thing that comes to mind is the content related to Microsoft's Desktop Bridge. After looking around, I found a way for Win32 to call UWP Api. However, the APIs that can be called are limited, and the documentation is relatively incomplete. The most troublesome thing is to correct Win32 Project configuration modification, introducing a bunch of WinRT stuff. After trying for a long time, finally no error is reported, but it will crash when running. The reason is unknown and needs to be further explored. What is more suspicious is that there are contradictions in the official documents. It is not clear whether the Windows.System.Launcher Api we use is supported by this calling method, because we cannot verify the error report.

Interested partners can refer to the following links:

Desktop Bridge

Detect UWP App

Solution 2

Simple and rude, directly detect the installation directory of UWP. Generally, the default installation path of UWP is "C:\Program Files\WindowsApps". This method is really simple and crude, but it has several disadvantages:

  1. There may be obsessive-compulsive users who have modified the UWP installation path. In this case, you need to check the registry yourself, of course, what is the registry key value, you need baidu;
  2. If you directly enumerate the subdirectories of "C:\Program Files\WindowsApps", there will be permission problems (System). Ordinary user permissions can only access files like "C:\Program Files\WindowsApps\microsoft.windowscommunicationsapps_17.9126.21695.0_x64__8wekyb3d8bbwe" Specific UWP App directory, which requires us to determine in advance the pfn (package family name, UWP App specific identifier, unique globally) and version of the UWP App to be found, but the version is often changed, and it is difficult to determine.

Solution 3 (Solution 1 is similar to this)

Microsoft provides us with many ways to start UWP, such as what protocol to start, command line to start, etc., but the premise of using these methods is: our UWP app needs to modify the existing App Manifest, which is for UWP apps that have been released, Obviously not possible. (In our scenario, because our UWP App is bound to the driver, it is generally upgraded with the driver and is relatively stable, so this method is not available)

Solution 4 (Best solution)

I vaguely remember that when I used Fiddler before, there was a WinConfig function that listed all UWP programs on the current computer (actually sandbox programs, starting from Windows 8, UWP is also included), and then web debugging was possible. So I wondered if I could learn from Fiddler's approach. Then I found that there is a program named EnableLoopback.exe under the Fiddler installation directory. For no reason, I threw it into ILSpy and decompiled the C# code perfectly. After some exploration, I found AppContainer The class, whether looking at the class name or the class definition, is very clear, this is what we are looking for, and then follow this class to find its way to get all UWP programs: through the interface NetworkIsolationEnumAppContainers in FirewallAPI.dll enumerate.

AppContainer

With understanding, start Coding!

BTW, if you want to save trouble, you can directly export the content related to this class, which can be used directly. But our Win32 is written in C++, so we need to convert it a bit.

The C++ code is as follows:

#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 dolbyApp = uwpApps.at(j);
                transform(dolbyApp.begin(), dolbyApp.end(), dolbyApp.begin(), tolower);
                if (dolbyApp == appContainer.appContainerName)
                {
                    //launch it;
                    auto temp = uwpApps.at(j);
                    LaunchSpecifcApp(&temp);
                }
            }
        }
        FreeAppContainersProc(ppPublicAppCs);
        FreeLibrary(FirewallAPIModule);
        vector<wstring>().swap(uwpApps);
    }
}

The code is very straightforward. There are two functions in it, one is used to find, the other is used to start, and the additional content is related to Win32 Dll calls.

finally

As you can see, it is more troublesome for us to find UWP, but the call is very simple. The core is:

"explorer.exe shell:AppsFolder\\{pfn}!App"

Very straightforward, a naked shortcut! But there are pitfalls. If there is any problem with the passed parameters (either misspelled or non-existent), explorer will directly ignore the parameters and start itself. This kind of behavior, for users who don't know the truth, will be very inexplicable and garbage software. So when we start our UWP App, we must make sure that our App must exist on the user's computer, so we have the above logic to detect UWP App. If the parameters are wrong and explorer dares nothing, we will not be so troublesome, we can directly start all our UWP apps one by one, simple and rude!

last of the last

We used the method used in Fillder, but I don't know about the various issues of Fiddler's copyright. Fortunately, we implemented it directly in C++ without any impact. Right to learn to learn!

The source code of the Fiddler 2.x version was available on the Internet before, but it is not clear whether this software is open source.

Hats off to Fiddler!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324936680&siteId=291194637