Launching Windows Store Apps Programmatically

October 24, 2015

2 comments

Windows Apps (a.k.a. Store apps or Metro apps) run in a dedicated sandbox, providing “extra protection” from outside apps and OS as opposed to classic Windows applications. One consequence of this is that launching a Windows App using a classic CreateProcess call will generally fail. For example, if we run the Weather app that comes with Windows and look at the command line that was used to start the process (e.g. using Task Manager or Process Explorer), this is what we see:

"C:\Program Files\WindowsApps\Microsoft.BingWeather_4.6.169.0_x86__8wekyb3d8bbwe\Microsoft.Msn.Weather.exe"
     -ServerName:App.AppX2m6wj6jceb8yq7ppx1b3drf7yy51ha6f.mca

Clearly, there is a command line argument of some sort that is required. We may think that it’s not too bad – all we need is to find out that crazy string appended to ServerName and we can launch that kind of process ourselves. Alas, if we try this particular example from a command window we get a nasty error:

image

This means that using the traditional CreateProcess call is simply not enough. And if we think about it – it kinda makes sense. At creation time the process must already be in the sandbox, otherwise it may be able to “escape” it. There is a special kernel component (PLM – Process Lifecycle Management) that is responsible for managing such apps and building the sandbox App Container.

Launching Windows (Store) Apps

In the Windows 8.x time frame there were some attempts to launch Windows Apps by digging into the registry where such apps are registered and then use a tool called “AppxLauncher” (part of the certification kit) to do the actual launching. This approach worked in Windows 8.x, but it has two issues: first, it breaks in Windows 10 as the registry layout for store apps changed, and second, the AppxLauncher tool may or may not exist on the system. There must be a robust way to launch such apps.

After some searching, I found the IApplicationActivationManager COM interface, that seems to be the “right” way to launch Store apps. This is a classic COM interface (not WinRT). Here’s its definition converted to C#:

enum ActivateOptions {
    None = 0x00000000,  // No flags set
    DesignMode = 0x00000001,  // The application is being activated for design mode
    NoErrorUI = 0x00000002,  // Do not show an error dialog if the app fails to activate                               
    NoSplashScreen = 0x00000004,  // Do not show the splash screen when activating the app
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("2e941141-7f97-4756-ba1d-9decde894a3d")]
interface IApplicationActivationManager {
    int ActivateApplication([MarshalAs(UnmanagedType.LPWStr)] string appUserModelId, [MarshalAs(UnmanagedType.LPWStr)] string arguments,
        ActivateOptions options, out uint processId);
    int ActivateForFile([MarshalAs(UnmanagedType.LPWStr)] string appUserModelId, IntPtr pShelItemArray,
        [MarshalAs(UnmanagedType.LPWStr)] string verb, out uint processId);
    int ActivateForProtocol([MarshalAs(UnmanagedType.LPWStr)] string appUserModelId, IntPtr pShelItemArray,
        [MarshalAs(UnmanagedType.LPWStr)] string verb, out uint processId);
}

The simplest form for app launch is ActivateApplication that seems simple enough, requiring something called AppUserModelId. What is that?

The concept of AppUserModelId was conceived in Windows 7 to get some of the functionality that we now take for granted, such as the features we observe in the Taskbar – jump lists, window grouping and more. All these rely on a unique ID that may technically span applications; conversely, such IDs can be different for different windows belonging to the same app. You can read more about this in the MSDN docs.

The same concept is now used for Windows Store apps as well. This means that to launch a particular app we need somehow to locate that ID. It seems logical that these IDs are stored somehow in the system registry to be located by Explorer.Exe or possibly some other component involved in App launching.

There are a variety of ways to get a AppUserModelId. For example, the GetApplicationUserModelId API function can be used to get the appUserModeld given a handle to a process. For our purposes, we want the ID given a package. This can be obtained using GetPackageApplicationIds; once we have that we can use the aforementioned interface. Before calling GetPackageApplicationIds we need to open a “handle” to a package given its full name using OpenPackageInfoByFullName. Here’s both functions P/Invoke definitions, along with a ClosePackageInfo to match the open:

[DllImport("kernel32")]
static extern int OpenPackageInfoByFullName([MarshalAs(UnmanagedType.LPWStr)] string fullName, uint reserved, out IntPtr packageInfo);

[DllImport("kernel32")]
static extern int GetPackageApplicationIds(IntPtr pir, ref int bufferLength, byte[] buffer, out int count);

[DllImport("kernel32")]
static extern int ClosePackageInfo(IntPtr pir);

The tricky part is that buffer passed to GetPackageApplicationIds, since it can contain multiple IDs (I’m not sure what scenario this covers) as an array of pointers to Unicode strings. Here’s some code that gets the first ID given a full package name (error handling omitted for brevity):

public static uint LaunchApp(string packageFullName, string arguments = null) {
    IntPtr pir = IntPtr.Zero;
OpenPackageInfoByFullName(packageFullName, 0, out pir);

    int length = 0, count;
    GetPackageApplicationIds(pir, ref length, null, out count);

    var buffer = new byte[length];
    GetPackageApplicationIds(pir, ref length, buffer, out count);

    var appUserModelId = Encoding.Unicode.GetString(buffer, IntPtr.Size * count, length - IntPtr.Size * count);

 

OpenPackageInfoByFullName opens a “connection” to a package, returning an opaque handle to the package. Then, GetPackageApplicationIds is called twice: first, with a zero length to get the required length for the buffer, and then after the buffer is allocated – a second call with the returned length. The count variable returns the number of IDs (I always encountered one, but the code just takes the first one even if more than one exists). Extracting the ID is done with the UnicodeEncoding.GetString method starting with the correct offset, taking into account that the buffer contains string pointers to locations further down the buffer; the strings themselves are located right after the array of pointers.

Once we have the appUserModelID, we can simply pass it to IApplicationActivationManager.ActivateApplication. The COM class implementing this interface has the following GUID:

[ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")]
class ApplicationActivationManager { }

The rest of the LaunchApp method goes like this:

    var activation = (IApplicationActivationManager)new ApplicationActivationManager();
    uint pid;
    int hr = activation.ActivateApplication(appUserModelId, arguments ?? string.Empty, ActivateOptions.NoErrorUI, out pid);
    if(hr < 0)
        Marshal.ThrowExceptionForHR(hr);
    return pid;
}

The ActivateApplication method is kind enough to return the process ID of the newly created process should that prove useful.

I’ve created a simple app called MetroManager on GitHub that lists all packages for the current user and allows her to launch selected packages. The code uses the WinRT class PackageManager with a call to FindPackagesForUser that returns a list of packages. For each package, various properties are available, among them the full package name that is required for OpenPackageInfoByFullName. Here’s a screenshot:

image

The selected package can be launched (assuming it’s not a framework) by clicking the Launch button or simply double-clicking.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

2 comments

  1. sergeynApril 21, 2016 ב 09:33

    Hi Pavel,
    I need to use some WinRT UI code from inside a dll launched by desktop application, i.e. – a plugin reusing some ui from standalone app. Your knowledge about internal machinery would really help to make it to work. Would you help me out?
    Thanks.

    Reply
  2. ZiNNEDJanuary 19, 2017 ב 11:49

    Very nice! Helped me a lot with a project of mine in which I needed to be able to open Metro apps in Windows 10 from within my own WPF app.

    Thank you very much for sharing this!

    Reply