Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Run Windows Store Apps From Desktop

0.00/5 (No votes)
25 Mar 2013 1  
How to work with Windows Store apps from a Desktop application - Run, Get App State, Stop, and more.

Introduction

When customers ask me how they can run a Windows Store app from Desktop usually the answer is – You Can’t, but if you really want to, there is a way to do that.

The reason I usually answer You Can’t is because – in order to run a Windows Store app from Desktop you need to install Windows App Certification Kit, this pack contains the “microsoft.windows.softwarelogo.appxlauncher.exe” file that can run a Windows Store app by this application model ID.

So if you plan on publishing your app you can’t assume the ACK is installed on the client machine.

Again, if you really want… Let me show you.

Step 1: Getting Started 

First create a WPF project and add the following reference: “C:\Program Files (x86)\Windows Kits\8.0\App Certification Kit\microsoft.windows.softwarelogo.shared.dll”.

Step 2: Getting the Windows App List

The reason you need to add a reference to “Microsoft.Windows.Softwarelogo.Shared.dll” is so we can receive the program inventory XML file. This file contains the complete list of all installed Windows Store apps.

Under my PC, here is the file location:

"C:\\Users\\Shai\\AppData\\Local\\Microsoft\\AppCertKit\\programinventory_e25bb752-e7cf-4fb2-8194-874ba9b91c7b.xml"

As I said, this file contains all Windows Store apps installed on your machine, each Program element under that file will show all the information regarding that specific app.

So, how do you get the file location?

It’s very simple; using the GlobaldataAccessor method from softwarelogo.shared.dll you can get the Program Inventory Location string.

string itemValue = GlobalDataAccessor.GetItemValue("ProgramInventoryLocation", "PRE_PROCESS"); 

Once you have got this file all you need is parse the XML and create a collection of apps.

I've create a ProductInfo class that will represent each program in the file. As you can see from the code below I simply take the attributes and elements from the Program element.

public class ProductInfo : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    private string _Status;
    public string Status
    {
        get { return _Status; }
        set
        {
            if (value != this._Status)
            {
                this._Status = value;
                NotifyPropertyChanged("Status");
            }
        }
    }
    public string LogoUrl { get; set; }
    public string ProductName { get; set; }
    public string ProductVendor { get; set; }
    public string ProductLanguage { get; set; }
    public string ProductVersion { get; set; }
    public string RootDirPath { get; set; }
    public string Id { get; set; }
    public string PackageFullName { get; set; }
    public ProductInfo(XmlNode xNode)
    {
        var attrib = xNode.Attributes["Name"];
        if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) ProductName = attrib.Value;
        attrib = xNode.Attributes["Version"];
        if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) ProductVersion = attrib.Value;
        attrib = xNode.Attributes["Language"];
        if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) ProductLanguage = attrib.Value;
        attrib = xNode.Attributes["Publisher"];
        if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) ProductVendor = attrib.Value;
        attrib = xNode.Attributes["RootDirPath"];
        if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) RootDirPath = attrib.Value;
        attrib = xNode.Attributes["Id"];
        if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) Id = attrib.Value;
        var node = xNode.SelectSingleNode("/Log/ProgramList/Program[@Id='" + this.Id + 
          "']/Indicators/PackageManifestIndicator/Properties/Logo");
        if (node != null && !string.IsNullOrEmpty(node.InnerText))
        {
            var imgUrl = System.IO.Path.Combine(RootDirPath, node.InnerText);
            if (File.Exists(imgUrl))
                LogoUrl = imgUrl;
        }
        node = xNode.SelectSingleNode("/Log/ProgramList/Program[@Id='" + this.Id + 
          "']/Indicators/PackageManifestIndicator/PackageManifest");
        if (node != null && !string.IsNullOrEmpty(node.Attributes["PackageFullName"].InnerText))
        {
            PackageFullName = node.Attributes["PackageFullName"].InnerText;
        }
    }
} 

And now, let’s connect the two parts together. The first thing is getting the ProgramInventorylocation and after that just load that XML file and parse it to objects with the information you want.

private void BuildAppsList()
{
    string itemValue = GlobalDataAccessor.GetItemValue("ProgramInventoryLocation", "PRE_PROCESS");
    XmlNodeList list = null;
    var reportDoc = new XmlDocument();
    reportDoc.Load(itemValue);
    ProductList = new ObservableCollection<ProductInfo>();
    list = reportDoc.GetElementsByTagName("Program");
    if (list.Count < 1)
    {
        throw new XmlException();
    }
    foreach (XmlNode node in list)
    {
        ProductInfo item = new ProductInfo(node);
        ProductList.Add(item);
    }
    dbTable.ItemsSource = ProductList;
}

Step 3: Get App User Model Id

Now after you have got all Windows Store apps installed on your machine it’s time to run them. In order to run a Windows Store app you’ll need to obtain the AppUserModelId, the reason you need AppUserModelId is because the appxlauncher.exe needs this value in order to launch the application (Package Name is not enough).

After completing Step 2 we got the App Package Full Name, we need to use this value to find the AppUserModelId from the Registry.

HKEY_CURRENT_USER\Software\Classes\ActivatableClasses\Package\**PackageFullName**\Server

So let’s add the following method. This method receives a packageFullName string and performs a search in the registry for the AppUserModelId.

public static string GetAppUserModelId(string packageFullName)
{
    string str = string.Empty;
    using (RegistryKey key = Registry.CurrentUser.CreateSubKey(string.Format(
       @"SOFTWARE\Classes\ActivatableClasses\Package\{0}\Server\",
       packageFullName)))
    {
        if (key == null) return str;
        var appKeys = from k in key.GetSubKeyNames()
                        where !k.StartsWith("BackgroundTransferHost")
                        select k;
        foreach (var appKey in appKeys)
        {
            using (RegistryKey serverKey = key.OpenSubKey(appKey))
            {
                if (serverKey.GetValue("AppUserModelId") != null)
                {
                    str = serverKey.GetValue("AppUserModelId").ToString();
                    serverKey.Close();
                    break;
                }
            }
        }
    }
    return str;
} 

Step 4: Running Windows Store App

After we have the AppUserModelId string for a specific Windows Store app, we can run it.

You can test it by opening the command line and writing the following:

C:\Program Files (x86)\Windows Kits\8.0\App Certification Kit\
          Microsoft.Windows.SoftwareLogo.AppxLauncher.exe “AppUserModelId

Start Button Code:

private void StartApp_Click(object sender, RoutedEventArgs e)
{
    var product = ((System.Windows.Controls.Button)sender).Tag as ProductInfo;
    var appUserModelId = Helpers.GetAppUserModelId(product.PackageFullName);
    var exec = @"C:\Program Files (x86)\Windows Kits\8.0\App Certification Kit\Microsoft.Windows.SoftwareLogo.AppxLauncher.exe";
    if (!File.Exists(exec))
    {
        System.Windows.MessageBox.Show("Please install Windows App Certification Kit for Windows RT");
    }
    var processInfo = new ProcessStartInfo()
    {
        Arguments = appUserModelId,
        UseShellExecute = false,
        CreateNoWindow = true,
        FileName = exec
    };
    Process.Start(processInfo);
} 

Step 5: Get Application Status  

The last thing you might want is to know the app execution state. In order to do that you need to use IPackageDebugSettings - enables debugger developers control over the lifecycle of a Windows Store app, such as when it is suspended or resumed (http://msdn.microsoft.com/en-us/library/hh438393(v=vs.85).aspx).

For that, create a PackageStatushelper class with the following code:

public class PackageStatusHelper
{
    [ComImport, Guid("B1AEC16F-2383-4852-B0E9-8F0B1DC66B4D")]
    public class PackageDebugSettings
    {
    }
    public enum PACKAGE_EXECUTION_STATE
    {
        PES_UNKNOWN,
        PES_RUNNING,
        PES_SUSPENDING,
        PES_SUSPENDED,
        PES_TERMINATED
    }
    [ComImport, Guid("F27C3930-8029-4AD1-94E3-3DBA417810C1"), 
                     InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IPackageDebugSettings
    {
        int EnableDebugging([MarshalAs(UnmanagedType.LPWStr)] string packageFullName, [MarshalAs(UnmanagedType.LPWStr)]
                                                              string debuggerCommandLine, IntPtr environment);
        int DisableDebugging([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
        int Suspend([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
        int Resume([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
        int TerminateAllProcesses([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
        int SetTargetSessionId(int sessionId);
        int EnumerageBackgroundTasks([MarshalAs(UnmanagedType.LPWStr)] string packageFullName, 
                                                      out uint taskCount, out int intPtr, [Out] string[] array);
        int ActivateBackgroundTask(IntPtr something);
        int StartServicing([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
        int StopServicing([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
        int StartSessionRedirection([MarshalAs(UnmanagedType.LPWStr)] string packageFullName, uint sessionId);
        int StopSessionRedirection([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
        int GetPackageExecutionState([MarshalAs(UnmanagedType.LPWStr)] string packageFullName,
                                            out PACKAGE_EXECUTION_STATE packageExecutionState);
        int RegisterForPackageStateChanges([MarshalAs(UnmanagedType.LPWStr)] string packageFullName, 
                               IntPtr pPackageExecutionStateChangeNotification, out uint pdwCookie);
        int UnregisterForPackageStateChanges(uint dwCookie);
    }
    public static PACKAGE_EXECUTION_STATE GetPackageExecutionState(string packageFullName)
    {
        PACKAGE_EXECUTION_STATE packageExecutionState = PACKAGE_EXECUTION_STATE.PES_UNKNOWN;
        PackageDebugSettings settings = new PackageDebugSettings();
        IPackageDebugSettings settings2 = (IPackageDebugSettings)settings;
        try
        {
            if (settings2.GetPackageExecutionState(packageFullName, out packageExecutionState) != 0)
            {
                System.Windows.MessageBox.Show("Failed to get package execution state.", "GetPackageExecutionState");
            }
        }
        catch (Exception ex)
        {
            System.Windows.MessageBox.Show(ex.Message, "GetPackageExecutionState");
        }
        return packageExecutionState;
    }
} 

And from the application, you can just call GetPackageExecutionState passing the Package Full Name:

private void GetAppStatus_Click(object sender, RoutedEventArgs e)
{
    var btn = (System.Windows.Controls.Button)sender;
    var product = btn.Tag as ProductInfo;
    var status = PackageStatusHelper.GetPackageExecutionState(product.PackageFullName);
    switch (status)
    {
        case PackageStatusHelper.PACKAGE_EXECUTION_STATE.PES_RUNNING:
            btn.Foreground = new SolidColorBrush(Colors.Green);
            btn.Content = "Running";
            break;
        case PackageStatusHelper.PACKAGE_EXECUTION_STATE.PES_SUSPENDED:
            btn.Foreground = new SolidColorBrush(Colors.Orange);
            btn.Content = "Suspended";
            break;
        case PackageStatusHelper.PACKAGE_EXECUTION_STATE.PES_TERMINATED:
            btn.Foreground = new SolidColorBrush(Colors.Red);
            btn.Content = "Terminated";
            break;
        default:
            btn.Foreground = new SolidColorBrush(Colors.Gray);
            btn.Content = "Unkown";
            break;
    }
} 

Points of Interest 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here