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
Shai Raiten is VS ALM MVP, currently working for Sela Group as a ALM senior consultant and trainer specializes in Microsoft technologies especially Team System and .NET technology. He is currently consulting in various enterprises in Israel, planning and analysis Load and performance problems using Team System, building Team System customizations and adjusts ALM processes for enterprises. Shai is known as one of the top Team System experts in Israel. He conducts lectures and workshops for developers\QA and enterprises who want to specialize in Team System.
My Blog: http://blogs.microsoft.co.il/blogs/shair/