Mobile Corner

Enterprise Distribution of Windows Phone Applications

Nick Randolph discussed how Windows Phone applications can be deployed within a company using enterprise distribution.

One feature introduced in Windows Phone 8 is the ability to self-publish and distribute applications within an organization, bypassing the Windows Phone Store. Often referred to as enterprise or company distribution, it makes Windows Phone a great line-of-business tool, as applications can be rapidly built, tested, and distributed internally without any dependencies on external testing, certification, or publishing. This feature was extended in Windows Phone 8.1 to support both XAP and APPX packaging formats, which includes support for XAPs built against the Windows Phone 8.1 SDK.

In this article, you'll learn what you need in order to sign and distribute any Windows Phone application.

Enterprise Distribution Basics
You can distribute a Windows Phone application using enterprise distribution in two ways: either via a Mobile Device Management (MDM) tool, or by simply side-loading the application.

An MDM is typically used by organizations that want to provision and control functionality on devices connecting into the organization. A user enrolls the device into the organization (this process often starts on the phone by selecting Settings | Company/Workplace | Add Account), at which point the MDM typically pushes down any policies, certificates, accounts and applications that are required by the organization.

The alternative to using an MDM is that applications are hosted on a secured Web site (to prevent unauthorized access), or distributed via a rights-managed e-mail (to prevent forwarding outside the company). Clicking on the application link or attachment triggers the installation of the application. This method doesn't allow for effective management or updating of the application, so it's not recommended for large-scale deployments.

Both ways of performing enterprise distribution rely on a couple of points:

  • Applications being distributed need to be signed using an enterprise-signing certificate.
  • The signing certificate needs to be trusted by the devices to which the application is going to be deployed.

The enterprise-signing certificate used to sign applications needs to be sourced from Symantec (I'll come back to this) and is a .PFX file. This needs to be converted into an .AETX file using the AETGenerator tool that's part of the Windows Phone SDK (more on this, later). In the case of using an MDM, the .AETX file is typically uploaded into the MDM and deployed during the enrollment phase of a device. In the case of side-loading, the .AETX can either be attached to an e-mail or hosted on a Web site. Clicking on the attachment or the link on the Web page will trigger the file to install on the device. Both scenarios result in the device being set up, ready for enterprise-signed applications to be installed.

If an MDM is used, once an application has been signed it's then uploaded into the MDM. Each MDM is different in its implementation, but usually they provide a mechanism to then assign applications to individuals or groups of users or devices. The application is then available for users to install on their devices. If applications are being distributed without an MDM they can either be attached to an e-mail, or hosted on a Web site. Again, clicking on the attachment or the link will result in the application being installed.

Acquiring an Enterprise-Signing Certificate
Now, I'll show you how to create a Windows Phone Store Account and obtain an enterprise-signing certificate. Here are the steps, and one involves heading over to the Symantec site:

1. Create a Windows Phone Store Account
In order to acquire an enterprise-signing certificate, you first need a Windows Phone Store account. Sign up for a new account at dev.windows.com/join, or access your account at dev.windowsphone.com/dashboard. Be aware of a catch here: Your account needs to be a company account, not an individual -- make sure you select the right box, as it's extremely difficult to change it at a later stage.

2. Obtain an Enterprise Mobile Code-Signing Certificate
A code-signing certificate can only be purchased from Symantec. You'll need both your Symantec Publisher ID and the Primary Email Address of your Windows Phone Store account. These can be found by logging into your Windows Phone Store account and navigating to the Account tab, as shown in Figure 1. The Symantec Id field is the Symantec Publisher ID required as the first step of the certificate-request process. The Primary Email Address is found by clicking on the Edit link under the Contact info heading; under the Approver info is the Email address field. Use this value during the request process.

[Click on image for larger view.] Figure 1. Enter Your Symantec Id and E-mail Address

Note: Make a record of the computer and browser where the request was made, as you'll need to return to this browser to collect the certificate. Also, make sure you're not using in-private browsing mode, as this may prevent you from retrieving the certificate.

Upon completing the request process you should receive a confirmation e-mail with a series of links and instructions to follow. In short, make sure you follow all the instructions very carefully. Here's a summary of the main points:

  • Make sure you're in the same browser on the same computer where you made the certificate request.
  • Click on the links in the certificate confirmation e-mail to install both the root and intermediary certificates. These need to be added to the Trusted Root Certification Authorities and Intermediate Certification Authorities stores, respectively, on the machine, not the default store, on a PC, and should be marked as Always Trusted on a Mac (full instructions are here.)
  • Click on the link to retrieve the certificate and make sure it opens in the browser that made the certificate request. This will install the signing certificate into the certificate store (PC) or keychain (Mac). At this point open the certificate (run certmgr.msc on a PC or open the keychain manager on a Mac) and verify that it's fully trusted.
  • Export the certificate to a .PFX file. Make sure you include the private key and specify a password whilst exporting the certificate (full instructions are here.)

3. Validate Your Certificate
After exporting the .PFX file you can validate it by double-clicking on it and installing it, selecting a store other than the default. Open certificate manager (run certmgr.msc) and navigate to the store into which you installed the certificate. Locate the certificate by looking for certificates in the store issued by Symantec Enterprise Mobile CA for Microsoft. Open the certificate and look at the Certification Path tab, as shown in Figure 2.

[Click on image for larger view.] Figure 2. Open Up the Certificate to Validate It

The status of your certificate should say that the certificate is OK and you should see three nodes in the certificate path, the first two starting with Symantec and the last being your company name.

Signing Your First Application
Now I'll show you how to create and sign three applications that represent the different platform configurations you might have to deal with: Windows Phone 8 XAP, Windows Phone 8.1 XAP and Windows Phone 8.1 APPX. For each application, I'll show how to sign it, ready it for distribution, and then demonstrate how you can install them on either the emulator or a real device to validate that they've been correctly signed.

Important: There's a known issue with the final release of Visual Studio 2013 Update 2, which broke the signing of Windows Phone applications. This means you'll need to upgrade to Update 3, which should fix any signing issues you might encounter. If you see an error stating "Installation failed: Install failed. Please contact your software vendor. (Exception from HRESULT: 0x80073CF9)," you should upgrade to Update 3.

In Visual Studio, I've created an empty solution and added three Windows Phone applications based on the Hub App (Windows Phone) template, TestWP81Appx, the Panorama App (Windows Phone Silverlight) template, TestWP81Xap, and the Pivot App (Windows Phone Silverlight) template, TestWP8Xap. When creating TestWP81Xap and TestWP8Xap I selected the appropriate platform version, Windows Phone 8.1 and Windows Phone 8, respectively, when prompted. Out of the box these applications are all able to run in the emulator or on a developer-unlocked device. I'll now sign each of these applications so they can be distributed to any Windows Phone.

Signing a Windows Phone 8 Silverlight Application: TestWP8Xap
The distribution package for a Windows Phone Silverlight application (for both TestWP8Xap and TestWP81Xap projects) is a .XAP file. When you do a build within Visual Studio, this file is automatically generated and can be found by default in the appropriate build configuration sub-folder under /bin (/bin/debug when building in Debug and /bin/release when building in Release mode). The .XAP file is typically named after the build configuration and platform selected; for example, TestWP8Xap_Debug_AnyCPU.xap implies it was built in Debug, targeting Any CPU.

There are multiple steps involved in preparing and signing an application ready for enterprise distribution. However, this has been simplified into a set of Windows PowerShell scripts, one for each platform configuration:

1. Windows Phone 8 .XAP:
C:\Program Files (x86)\Microsoft SDKs\WindowsPhone\v8.0\Tools\MDILXAPCompile\BuildMDILXap.ps1
2. Windows Phone 8.1 .XAP:
C:\Program Files (x86)\Microsoft SDKs\WindowsPhone\v8.1\Tools\MDILXAPCompile\BuildMDILSL81XAP.ps1
3. Windows Phone 8.1 .APPX:
C:\Program Files (x86)\Microsoft SDKs\WindowsPhoneApp\v8.1\Tools\MDILXAPCompile\BuildMDILAPPX.ps1

In this case, you want to use the signing script for Windows Phone 8 .XAP. The signing process typically creates some additional files, so it's worth placing both the file to be signed and the signing certificate in a folder by themselves -- this will help locate the signed file and review any log information if signing doesn't complete successfully.

To invoke the signing script you can either use the Windows PowerShell prompt, or use a regular command prompt. From a command prompt the Windows PowerShell command to invoke requires the path to the file to be signed, the path to the signing certificate and the password, in this case `XXXXX', on the certificate file (which I set when I exported the certificate after acquiring it from Symantec):

powershell.exe -ExecutionPolicy Unrestricted -File "c:\program files (x86)\Microsoft SDKs\Windows Phone\v8.0\Tools\MDILXAPCompile\BuildMDILXap.ps1" -xapfilename "C:\temp\entdistsigning\TestWP8Xap_Debug_AnyCPU.xap" -pfxfilename "c:\temp\entdistsigning\mycertificate.pfx" -password XXXXX

Running this command will indicate success or failed to complete the signing process -- this is easy to determine because the output messages are colored green (success) or red (failure). After running this command, three new files are created in the same folder as the original .XAP file: MDILOutput.log, XapsigntoolOutput.log and TestWP8Xap_Debug_AnyCPU_new.xap. The two log files are useful if the signing process doesn't complete successfully. The new .XAP file is signed, ready for distribution. Right-clicking on this .XAP file and selecting Properties will display additional information about the Digital Signature used to sign the application, as shown in Figure 3.

[Click on image for larger view.] Figure 3. Digital Signature for Signed Application

In addition to signing the .XAP file, this process also signs any .DLL files contained in the .XAP. Changing the .XAP file extension to .ZIP allows the contents of the application to be extracted; in the Properties window of the TestWP8Xap.dll there's also a Digital Signatures tab that contains the same signature information as the .XAP. It's also worth observing that this process doesn't encrypt the .XAP file or its contents.

Signing a Windows Phone 8.1 Silverlight Application: TestWP81Xap
Signing a Windows Phone 8.1 .XAP file is very similar. The output file when doing a normal Build in Visual Studio is a .XAP file, again located in the appropriate /bin sub-folder, named according to the build configuration and platform architecture (TestWP81Xap_Debug_AnyCPU.xap). You invoke the following command to sign the Windows Phone 8.1 .XAP file:

powershell.exe -ExecutionPolicy Unrestricted -File "c:\program files (x86)\Microsoft SDKs\Windows Phone\v8.1\Tools\MDILXAPCompile\BuildMDILSL81XAP.ps1" -xapfilename "c:\temp\entdistsigning\TestWP81Xap_Debug_AnyCPU.xap" -pfxfilename "c:\temp\entdistsigning\mycertificate.pfx" -password XXXXX -inputFolder "C:\temp\entdistsigning\input" -outputfolder "C:\temp\entdistsigning\output"

The syntax of this command is slightly different in that you can specify an input and output folder. During execution, this command will create these folders if they don't already exist. The contents of the original .XAP file are extracted into the inputFolder. The outputFolder is used to prepare and sign contents in order to create the final .XAP file, which is created in a sub-folder based on the name of the .XAP file (TestWP81Xap_Debug_AnyCPU_optimized), along with three log files that cover the stages of the signing process. Right-clicking on the newly created .XAP and selecting Properties will again reveal the Digital Signatures tab with the information about the signing certificate.

There are a couple of other points worth observing during the signing process. First, the output is color-coded to indicate success (green), failure (red) and any warnings. In the case of this simple example, the following warning notice is observed:

"NO APPX ASSEMBLIES FOUND IN THIS SL 8.1 XAP"

This can safely be ignored because you're not using any Windows Phone 8.1 functionality, which requires external assemblies (background tasks, for example). However, it's important to monitor the output of the signing process because it can often point to an issue with your application that you'd otherwise not be aware of until you attempt to distribute it.

At the end of the signing process, you'll be prompted to delete the input and output folders. Under normal conditions you can elect to delete these folders. As the signed .XAP is placed in the outputFolder this folder isn't deleted, only the sub-folders used during the signing process are deleted. If you're attempting to work out why your application isn't able to be distributed, or diagnose errors during signing, you should elect not to delete these folders so that you can examine the contents for any issues.

Signing a Windows Phone 8.1 Application: TestWP81Appx
The Windows Phone 8.1 .APPX application is the last that needs to be signed. Unlike the .XAP files that are automatically generated by Visual Studio when invoking Build, in this case to generate the deployment package (that is, the .APPX file), You need to select Store | Create App Packages from the Project menu shown in Figure 4. If this menu item doesn't appear or is disabled, make sure the .APPX project is selected in Solution Explorer by clicking on it and then go back to the Project menu.

[Click on image for larger view.] Figure 4. Creating the .APPX File

The Create App Packages dialog will walk you through preparing the application for submission to the Windows Phone Store. However, this application isn't going to be submitted to the store, so on the opening page of the dialog, when prompted as to whether you want to build packages to upload to the Windows Phone Store, select No. Proceed through the remaining steps, accepting the default options. Upon completion you're presented with a summary page, including a link to the folder where the .APPX file has been created. Actually, the folder contains an .APPXUPLOAD file along with a sub-folder, TestWP81Appx_1.0.0.0_AnyCPU_Debug_Test, which contains the actual .APPX file, TestWP81Appx_1.0.0.0_AnyCPU_Debug.appx (note that in addition to the build configuration and platform architecture the file name also includes the version number).

If you're looking for the signing Windows PowerShell script in a similar location to the 8 and 8.1 .XAP signing scripts, you might be surprised they're not in the Windows Phone sub-folder of Microsoft SDKs. Luckily, it's not far away in the WindowsPhoneApp sub-folder, as shown in the following command used to sign the .APPX file:

powershell.exe -ExecutionPolicy Unrestricted -File "c:\program files (x86)\Microsoft SDKs\WindowsPhoneApp\v8.1\Tools\MDILXAPCompile\BuildMDILAPPX.ps1" -appxfilename "C:\temp\entdistsigning\TestWP81Appx_1.0.0.0_AnyCPU_Debug.appx" -pfxfilename "c:\temp\entdistsigning\mycertificate.pfx" -password XXXXX -inputFolder "C:\temp\entdistsigning\input" -outputfolder "C:\temp\entdistsigning\output"

Again, the inputFolder and outputFolder are used during the signing process, and the signed .APPX will appear in a sub-folder under the outputFolder. The file Properties will again confirm that it has a signature that matches the signing certificate.

Installing Your Signed Applications
Now that the three applications have been correctly signed they can be deployed to a device (or emulator). The only prerequisite is that the certificate used to sign the applications is trusted by the device. In order for a device to trust the digital signature on a signed application, the certificate used to sign the application needs to be installed on the device. Actually, it's not the certificate (.PFX) that needs to be installed, but the enrollment token (.AETX) that matches the signing certificate. The .AETX can be created using the AETGenerator which requires the path to the .PFX file and the corresponding password, as follows:

"c:\program files (x86)\Microsoft SDKs\Windows Phone\v8.1\tools\AETGenerator\AetGenerator.exe" c:\temp\entdistsigning\mycertificate.pfx XXXXX

This command will generate three files: AET.XML, AET.AETX and AET.AET. For the purpose of manually installing signed applications, use the AET.AETX file.

In order to install the AET.AETX file and the signed application on the device, they need to be either received via e-mail, or they need to be placed on a Web site that's accessible from the device. One option is to set up a local IIS instance and create a virtual directory that you can copy files into and out of, thus making it easy for testing locally using a device or the emulator. Navigating to the .AETX file will prompt to confirm that the user wishes to add the workplace account, as shown in Figure 5.

[Click on image for larger view.] Figure 5. Opening the .AETX File

This prompt is a little misleading as it doesn't create an account that can be edited or removed. Instead, it simply means that applications signed with the signing certificate used to generate the .AETX file can be installed on that device.

After the .AETX file has been installed, the same process can be used to install any of the signed applications. In Figure 6 the Windows Phone 8.1 .APPX file, TestWP81Appx.appx, was opened on the device, in this case from a URL, but it could've been attached to an e-mail from which to open.

[Click on image for larger view.] Figure 6. Installing an Application

Clicking the install option will cause the application to be installed and will appear in the applications list. Note that there's no success/failure prompt to indicate whether the application has been installed. In addition, it can take up to a minute for the application to appear in the applications list, depending on how many applications you have installed on the device and the size and complexity of the application you're installing.

Automating the Installation of Applications
In addition to being able to manually install signed applications, there are also APIs available to install applications, query the list of applications already installed and launch an application that's been installed. The scope of these APIs is limited to applications from the same publisher, which essentially means those applications are signed with the same signing certificate.

I'm going to put together a simple application that queries for the list of applications already installed and displays them in a list. Tapping on an application in the list will launch that application. The application will also search the SD card (if it exists) for an application package and, if any exist, allow the user to install them.

Into the existing solution I've created another Windows Phone Silverlight application, AppPackageManager, targeting Windows Phone 8, as this will work across both Windows Phone 8 and 8.1, and allow for installation of both .XAP and .APPX packages. Listings 1 and 2 contain the source code for the page layout for the MainPage of the application and the codebehind file.

Listing 1: MainPage.XAML
<phone:PhoneApplicationPage
  x:Class="AppPackageManager.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  shell:SystemTray.IsVisible="True">
  <phone:PhoneApplicationPage.Resources>
    <DataTemplate
      x:Key="InstalledItemTemplate">
      <TextBlock
        Text="{Binding Id.Name}"
        Style="{StaticResource PhoneTextNormalStyle}"
        Margin="12" />
    </DataTemplate>
    <DataTemplate
      x:Key="PackagesItemTemplate">
      <TextBlock
        Text="{Binding Name}"
        Style="{StaticResource PhoneTextNormalStyle}"
        Margin="12" />
    </DataTemplate>
  </phone:PhoneApplicationPage.Resources>

  <Grid
    x:Name="LayoutRoot"
    Background="Transparent"
    d:DataContext="{Binding Source={StaticResource PackagesSampleData}}">
    <Grid.RowDefinitions>
      <RowDefinition
        Height="Auto" />
      <RowDefinition
        Height="*" />
    </Grid.RowDefinitions>
    <StackPanel
      x:Name="TitlePanel"
      Grid.Row="0"
      Margin="12,17,0,28">
      <TextBlock
        Text="APP PACKAGE MANAGER"
        Style="{StaticResource PhoneTextNormalStyle}"
        Margin="12,0" />
      <TextBlock
        Text="packages"
        Margin="9,-7,0,0"
        Style="{StaticResource PhoneTextTitle1Style}" />
    </StackPanel>

    <Grid
      x:Name="ContentPanel"
      Grid.Row="1"
      Margin="12,0,12,0">
      <Grid.RowDefinitions>
        <RowDefinition
          Height="Auto" />
        <RowDefinition />
        <RowDefinition
            Height="Auto" />
        <RowDefinition />
      </Grid.RowDefinitions>
      <TextBlock
        TextWrapping="Wrap"
        Style="{StaticResource PhoneTextLargeStyle}">
            <Run
          Text="Installed " />
            <Run
          Text="Applications" />
      </TextBlock>
      <phone:LongListSelector
        Grid.Row="1"
        ItemsSource="{Binding Existing}"
        ItemTemplate="{StaticResource InstalledItemTemplate}"
        SelectionChanged="ExistingSelectionChanged" />
      <TextBlock
        TextWrapping="Wrap"
        Text="Packages on SD Card"
        Grid.Row="2"
        Style="{StaticResource PhoneTextLargeStyle}" />
      <phone:LongListSelector
        Grid.Row="3"
        ItemsSource="{Binding Packages}"
        ItemTemplate="{StaticResource PackagesItemTemplate}"
        SelectionChanged="PackagesSelectionChanged" />

    </Grid>
  </Grid>
</phone:PhoneApplicationPage>
Listing 2: MainPage.xaml.cs
using System;
using System.IO;
using System.Windows;
using System.Windows.Navigation;
using Windows.ApplicationModel;
using Windows.Phone.Management.Deployment;
using Windows.Storage;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Storage;

namespace AppPackageManager
{
  public partial class MainPage
  {
    public MainPage()
    {
      InitializeComponent();
    }

    private async void PackagesSelectionChanged(object sender, 
      System.Windows.Controls.SelectionChangedEventArgs e)
    {
      // Find the selected SD card file
      var lls = (sender as LongListSelector);
      var package = lls.SelectedItem as ExternalStorageFile;
      if (package == null) return;
      lls.SelectedItem = null;

      // Copy the file to the local storage, 
      // replacing .betaxap with .xap and .betappx with .appx
      // by replacing "beta" with ""
      var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
        package.Name.Replace("beta", ""),
        CreationCollisionOption.ReplaceExisting);
      using (var read = await package.OpenForReadAsync())
      using (var write = await file.OpenStreamForWriteAsync())
      {
        await read.CopyToAsync(write);
      }

      // Need to create a Uri based on the absolute path to the file 
      // and then call AddPackageAsync
      var installUri = new Uri(file.Path, UriKind.RelativeOrAbsolute);
      try
      {
        await InstallationManager.AddPackageAsync(package.Name, installUri);
        MessageBox.Show("Installation complete");
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error: " + ex.Message);
      }
    }

    private void ExistingSelectionChanged(
      object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
      // Retrieve the selected installed package and launch it
      var lls = (sender as LongListSelector);
      var package = lls.SelectedItem as Package;
      if (package == null) return;
      lls.SelectedItem = null;
      package.Launch("");
    }

    protected async override void OnNavigatedTo(NavigationEventArgs e)
    {
      base.OnNavigatedTo(e);

      var vm = new MainViewModel();
      await vm.Load();
      DataContext = vm;
    }
  }
}

Listing 3 contains the code for MainViewModel, which exposes two collections that contain the installed applications (Existing) and the packages that are on the SD card, available for installation (Packages).

Listing 3: MainViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Windows.ApplicationModel;
using Windows.Phone.Management.Deployment;
using Microsoft.Phone.Storage;

namespace AppPackageManager
{
  public class MainViewModel
  {
    private ObservableCollection<Package> existing;

    public ObservableCollection<Package> Existing
    {
      get { return existing; }
    }

    private ObservableCollection<ExternalStorageFile> packages;

    public ObservableCollection<ExternalStorageFile> Packages
    {
      get { return packages; }
    }

    public async Task Load()
    {
      await LoadInstalledApplications();

      await LoadPackagesFromSDCard();
    }

    private async Task LoadPackagesFromSDCard()
    {
      // Connect to the current SD card
      var sdCard = (await ExternalStorage.GetExternalStorageDevicesAsync()).FirstOrDefault();

      // If the SD card is present, add GPX files to the Routes collection
      if (sdCard != null)
      {
        try
        {
          var folder = await sdCard.GetFolderAsync("Apps");
          var files = await folder.GetFilesAsync();
          packages = new ObservableCollection<ExternalStorageFile>(files);

        }
        catch (FileNotFoundException)
        {
          MessageBox.Show("The Apps folder is missing on your SD card");
        }
      }
      else
      {
        MessageBox.Show("The SD card is mssing.");
      }
    }

    private async Task LoadInstalledApplications()
    {
      try
      {
        var packages = InstallationManager.FindPackagesForCurrentPublisher();
        existing = new ObservableCollection<Package>(packages);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error: " + ex.Message);
      }
    }
  }
}

The SD card can be simulated via the emulator using the Additional Tools window, where a folder on the host computer can be mapped to the SD card. Mapping a folder will take a snapshot of what's in the folder and make it available to the emulator. In order to add/remove files from the SD card you first need to eject the SD card. This can yield the error shown in Figure 7 if you happen to have any files or folders in the mapped folder open in an application, including Solution Explorer. This is because during the eject process, all the files/folders contained within the SD card are copied back to the mapped folder. Close any open files/folders and try to eject the SD card again.

Figure 7. Error Ejecting the SD Card

In order to access contents from the SD card you need to give the application the ID_CAP_REMOVABLE_STORAGE capability and define the file types the application can read. Listing 4 includes the WMAppManifest.xml, which defines this capability and contains the FileTypeAssociation element. Unfortunately, both .XAP and .APPX are protected, so in order to pass package files into the application I'm using .BETAXAP and .BETAAPPX extensions. When a package is selected from the SD card to be installed it's first copied across into local storage with the correct file extension, before the AddPackageAsync command is invoked.

Listing 4: WMAppManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2012/deployment" AppPlatformVersion="8.0">
  <DefaultLanguage xmlns="" code="en-US" />
  <App xmlns="" ProductID="{e69b08f7-e9ff-4abe-b990-80816a41f7f4}" Title="AppPackageManager" 
    RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" 
    Author="AppPackageManager author" Description="Sample description" 
    Publisher="AppPackageManager" PublisherID="{7275f743-344e-418f-a8d5-4bdf2f1eea91}">
    <IconPath IsRelative="true" IsResource="false">Assets\ApplicationIcon.png</IconPath>
    <Capabilities>
      <Capability Name="ID_CAP_NETWORKING" />
      <Capability Name="ID_CAP_MEDIALIB_AUDIO" />
      <Capability Name="ID_CAP_MEDIALIB_PLAYBACK" />
      <Capability Name="ID_CAP_SENSORS" />
      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
      <Capability Name="ID_CAP_REMOVABLE_STORAGE" />
    </Capabilities>
    <Tasks>
      <DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
    </Tasks>
    <Tokens>
      <PrimaryToken TokenID="AppPackageManagerToken" TaskName="_default">
        <TemplateFlip>
          <SmallImageURI IsRelative="true" 
            IsResource="false">Assets\Tiles\FlipCycleTileSmall.png</SmallImageURI>
          <Count>0</Count>
          <BackgroundImageURI IsRelative="true" 
            IsResource="false">Assets\Tiles\FlipCycleTileMedium.png</BackgroundImageURI>
          <Title>AppPackageManager</Title>
          <BackContent>
          </BackContent>
          <BackBackgroundImageURI>
          </BackBackgroundImageURI>
          <BackTitle>
          </BackTitle>
          <DeviceLockImageURI>
          </DeviceLockImageURI>
          <HasLarge>
          </HasLarge>
        </TemplateFlip>
      </PrimaryToken>
    </Tokens>
    <Extensions>
      <FileTypeAssociation Name="Windows Phone SDK test file type" 
        TaskID="_default" NavUriFragment="fileToken=%s">
        <SupportedFileTypes>
          <FileType ContentType="application/xap">.betaxap</FileType>
          <FileType ContentType="application/appx">.betaappx</FileType>
        </SupportedFileTypes>
      </FileTypeAssociation>
    </Extensions>
    <ScreenResolutions>
      <ScreenResolution Name="ID_RESOLUTION_WVGA" />
      <ScreenResolution Name="ID_RESOLUTION_WXGA" />
      <ScreenResolution Name="ID_RESOLUTION_HD720P" />
    </ScreenResolutions>
  </App>
</Deployment>

If you run the AppPackageManager application from Visual Studio it will appear to run successfully. However, when you click on a file to install it you'll get a rather cryptic error stating that "An attempt was made to reference a token that does not exist" with an error code of 0x800703F0, as shown in Figure 8. This exception is raised because the AppPackageManager application isn't signed.

For this application to function correctly it needs to be signed using the same signing certificate as the applications it is to manage. Because it's a Windows Phone 8 .XAP application, use the same Windows PowerShell command as was used for the TestWP8Xap application:

[Click on image for larger view.] Figure 8. Missing Token Exception
powershell.exe -ExecutionPolicy Unrestricted -File "c:\program files (x86)\Microsoft SDKs\Windows Phone\v8.0\Tools\MDILXAPCompile\BuildMDILXAP.ps1" -xapfilename "c:\temp\entdistsigning\AppPackageManager_Debug_AnyCPU.xap" -pfxfilename "c:\temp\entdistsigning\mycertificate.pfx" -password XXXXX

Installing and running the signed AppPackageManager application will both show the correct list of other applications installed that were signed using the same certificate, and it will allow the installation of applications from the SD card.

Another issue you might run into whilst working with the emulator is that you may see an error similar to Figure 9.

[Click on image for larger view.] Figure 9. Can't Install Company App

This can occur for a number of reasons, but the most common are: the .AETX file hasn't been installed on the emulator or for some reason the workplace account has been revoked. The latter can happen if you attempt to install a package that hasn't been correctly signed or isn't a valid application package.

Figure 10 shows a different exception you might see if you sign your application packages using the scripts/signing tools that shipped with Visual Studio 2013 Update 2. Unfortunately, there was an issue with the release version of Update 2, which broke the signing capability for Windows Phone applications.

[Click on image for larger view.] Figure 10. Installation Failed

Important: For the purposes of signing Windows Phone applications, it's recommended to install Update 3 for Visual Studio 2013.

In this article, I've walked through the enterprise distribution capability of Windows Phone -- from acquiring the signing certificate to signing and distributing applications. While this illustrates how you can manually side-load applications, the recommended mechanism for distributing applications within an enterprise or workplace environment is via a Mobile Device Management tool. These tools provide a vast array of device and application management capabilities, over and above the ability to install applications. From this article you should understand the role application signing has and the steps involved in preparing your application for enterprise distribution.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube