Categories
Centennial

Fishing off the Desktop Bridge. Package Your Appx

Now that you’ve defined how your package will be structured, you need to build the AppX Package. Before getting to that though there are some things you’ll need…

Prerequisites

  • Windows 10 Anniversary update or later. I assume because the anniversary update include the infrastructure to run a Win32 app in a UWP container.
  • Windows 10 SDK. You’ll need this for the tools that will be covered below.
  • A code signing certificate. If you or your organization already have one you’re good to go. If not, create a self signing certificate for yourself.
  • Visual Studio 2015 Update 3. Visual Studio 2017 works too. We’ll use that in a later article for debugging the AppX (Right now it is still a Release Candidate so we’ll stick with 2015)
  • The complete example project is on GitHub.

Setup Visual Studio

We’re going to use build events in VS.NET to build the AppX. This is nice and easy. If you use MSBuild or some other build toolchain the same concepts apply.

Since we are going to use a couple of tools form the Windows SDK, we need to specify its location for the build event scripts. Open your csproj file in a text editor and add this declaration:

   <PropertyGroup>
    <Win10SDKDir Condition=" '$(Win10SDKDir)' == '' ">C:\Program Files (x86)\Windows Kits\10\bin\x64\</Win10SDKDir>
  </PropertyGroup>

Build Event Scripts

The simple package build approach we’ll take looks like this:

  1. After the build, copy all of the files that go into the package to a working folder named (appropriately enough) “AppX”.
  2. Use MakeAppX.exe to package the contents of that folder into a package file.
  3. Sign the package using signtool.exe.
  4. Use Powershell to install our package

Pre-build

Since we’ll rebuild the AppX package each time make sure we cleanup previous outputs at the start of a new build. All paths are relative to the $(TargetDir) location.

:: clean any previous package outputs
rmdir AppX /s /q
del $(TargetName).appx /q /f

Post-build

Let’s walk through the four phases of the build script and then put it all together in a single script at the end.

Collect the Package Contents

:: create the working folder for packaging
mkdir AppX

:: copy output, dependencies, and content into working a folder
xcopy "$(TargetPath)" AppX\ /R /Y
xcopy "$(TargetPath).config" AppX\ /R /Y
xcopy "$(TargetDir)Microsoft.HockeyApp.*.dll" AppX\ /R /Y
xcopy "$(SolutionDir)appxmanifest.xml" AppX\ /R /Y
xcopy "$(SolutionDir)*.png" AppX\ /R /Y

Since our example is pretty simple we’re just dumping everything in the folder root. Be sure to get everything your app needs into the working folder:

  • Build outputs: xcopy "$(TargetPath)"
  • Runtime configuration or data files: xcopy "$(TargetPath).config"
  • Binary dependencies: xcopy "$(TargetDir)Microsoft.HockeyApp.*.dll"
  • The AppXManifest (this needs to be in the root): xcopy "$(SolutionDir)appxmanifest.xml"
  • Content and other assets: xcopy "$(SolutionDir)*.png"

I’ve got the AppXManifest and images in the solution directory so modify those locations as appropriate.

Build and Sign the Package

Next we’re going to use the Windows 10 SDK to make the package and get it ready for installation.
MakeAppX.exe is the sdk tool the generates the package file.

"$(Win10SDKDir)MakeAppX.exe" pack /d AppX /p "$(TargetName).appx"
  • The pack command tells it to create the package
  • /d specifies the working folder to pack
  • /p is the package file name

This will create the package file in the build out directory $(TargetDir). Next we’ll sign the package using SignTool.exe. In this example case I’ve got a signing certificate named tempcastore.pfx, in the solution folder.

"$(Win10SDKDir)SignTool.exe" sign -f "$(SolutionDir)tempcastore.pfx" -fd SHA256 -p YOUR_PFX_PASSWORD -v .\$(TargetName).appx
  • The sign command tell it to the sign the input file
  • -f is the path to the signing certificate
  • -fd SHA256 tells it to use SHA256 for the digest algorithm
  • -p is the password for your pfx file
  • -v is the path to the file to sign

The result is a signed AppX package that we can install and run!

Install the Package

Because the Add-AppXPackage cmdlet won’t install a package with the same version on top of itself doing this in script takes a little finagling. The version Add-AppXPackage is concerned with is in the AppXManifest as an attribute on the Identity element. If you always update that version there’s no need to remove any previous packages. This isn’t convenient when working in the IDE so we’ll remove any previously installed packages in our build script.

powershell if ((Get-AppxPackage -Name '3652dkackman.FishyFishyFish').count -gt 0) { Remove-AppxPackage (Get-AppxPackage -Name '3652dkackman.FishyFishyFish').PackageFullName }
powershell Add-AppxPackage $(TargetName).appx

The first line invokes a powershell command that first checks if the package is installed and removes it if it is. The argument to both Get-AppxPackage and Remove-AppxPackage is the Identity.Name from your AppXManifest.

The second line uses Add-AppxPackage to install the package we just built. If everything worked out right, the package should now be available on your start menu.

Putting it All Together

The complete Post-build script should look like this:

:: create the working folder for packaging
mkdir AppX

:: copy build output, dependencies, and content into the working folder
xcopy "$(TargetPath)" AppX\ /R /Y
xcopy "$(TargetPath).config" AppX\ /R /Y
xcopy "$(TargetDir)Microsoft.HockeyApp.*.dll" AppX\ /R /Y
xcopy "$(SolutionDir)appxmanifest.xml" AppX\ /R /Y
xcopy "$(SolutionDir)*.png" AppX\ /R /Y

:: build and sign the AppX package
"$(Win10SDKDir)MakeAppX.exe" pack /d AppX /p "$(TargetName).appx"
"$(Win10SDKDir)SignTool.exe" sign -f "$(SolutionDir)tempcastore.pfx" -fd SHA256 -p YOUR_PFX_PASSWORD -v .\$(TargetName).appx

:: install it
powershell if ((Get-AppxPackage -Name '3652dkackman.FishyFishyFish').count -gt 0) { Remove-AppxPackage (Get-AppxPackage -Name '3652dkackman.FishyFishyFish').PackageFullName }
powershell Add-AppxPackage $(TargetName).appx

Now every time you change and build your code in the IDE it will be installed as a UWP app. F5 debugging will still start the normal build output not the AppX.

Debugging the AppX from the IDE will be our next step.

Categories
Centennial

Fishing Off the Desktop Bridge. Your AppXManifest

Start with the Documentation

The Desktop Bridge documentation has gotten better and better since the released the first preview. They have lot’s good articles, GitHub samples, snippets and guidance. I’ll try to cover some specifics and tie things together based on my experience. After you’ve brushed up on the documentation the next step is to generate an AppXManifest.xml. The AppXManifest describes the contents and meta data of a UWP AppX package.

Instead of using the Desktop App Converter (DAC), we will convert our app by hand. This way we’ll understand better what’s going on under the hood. Plus, unless your installation is exceedingly complex, doing this manually simplifies your build toolchain. Assuming that this isn’t a one time conversion, and you’ll be adding modern features to you Win32 app, you need to consider how you are going to iteratively build the project.

The DAC runs your installer in a Windows 10 VM, detects the system changes and spits out an AppX package. It looks like it does a really nice job of that but is a 3+ GB package (for the OS based image) and a bit of a black box. Depending on the needs of your deployment, the manual conversion may very well be fine.

Copy the Sample AppXManifest and Edit for Your App

Copy the sample Xml from the manual conversion documentation page in a blank AppXManifest.xml file and add it as a solution item in Visual Studio. Next, fill out all of the placeholders with the specifics of your app. You’ll end up with something that looks like:

<?xml version="1.0" encoding="utf-8"?>
<Package
   xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
   xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
   xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities">
  <Identity Name="3652dkackman.FishyFishyFish"
    ProcessorArchitecture="x86"
    Publisher="CN=D84FAA85-E110-4951-93B4-A797B4808678"
    Version="1.1.19.0" />
  <Properties>    
    <DisplayName>Fishy Fishy Fish</DisplayName>
    <PublisherDisplayName>dkackman</PublisherDisplayName>
    <Description>Some fish. Swimming around on your screen.</Description>
    <Logo>StoreLogo.png</Logo>
  </Properties>
  <Resources>
    <Resource Language="en-us" />
  </Resources>
  <Dependencies> 
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.14352.0" />
  </Dependencies>
  <Capabilities>
    <Capability Name="internetClient" />
    <rescap:Capability Name="runFullTrust"/>    
  </Capabilities>
  <Applications>
    <Application Id="FishyFishyFish" Executable="FishyFish.exe" EntryPoint="Windows.FullTrustApplication">      
      <uap:VisualElements
         BackgroundColor="transparent"
         DisplayName="Fishy Fishy Fish"
         Square150x150Logo="Square150x150Logo.png"
         Square44x44Logo="Square44x44Logo.png"
         Description="Some fish. Swimming around on your screen." />       
    </Application>
  </Applications>
</Package>

You’ll get real values for the Name and Publisher attributes of the Identity when you reserve the app name online. In the meantime, placeholder values of your choosing are fine. You will need to provide the 150 and 44 pixel logo images. as these are required assets. If you create a blank Universal app in visual studio you can hijack those png’s and make something better later on.

And finally, any place you see REALTIVE PATH referenced in the sample xml, it is a path relative to the root of your AppX package. An AppX file is just a Zip file and since Zip’s can have folder structure, make all those paths relative to the Zip/AppX file root. My app deploy is simple enough that I just have everything in the root but you might have an assets folder or the like to better organize your packages files.

So that’s the AppXManifest. Next we’ll turn it into an AppX package.