Run Windows Store Apps From Desktop

07/02/2013

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

The reason I usually answer is You Can’t is because – In order to run Windows Store app from Desktop you need to install Windows App Certification Kit, this pack contains “microsoft.windows.softwarelogo.appxlauncher.exe” file that can run a Windows Store app by his 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.

image

Download Demo Project

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 Windows App List

The reason you need to add reference to “Microsoft.Windows.Softwarelogo.Shared.dll” 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 app installed on you machine, each Program element under that file will show all the information regarding that specific app.

image

So, how do you get the file location?

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

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

Once you got this file all you need is parsing the XML and create a collection on 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 taking 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 part 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 got all Windows Store app 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 to 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 registry.

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

image

So let’s add the following method, this method receive a packageFullName string and perform 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 command line and write the following:

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

Start App Button

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 the 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;
    }
}

Download Demo Project

Enjoy

Add comment
facebook linkedin twitter email

Leave a Reply