Windows 10 Anniversary Update, Desktop App Converter Preview– Experimenting with Open Live Writer

Following on from my previous post;

Windows 10 Anniversary Update, Desktop App Converter Preview – Starting from Source

What I’d done in that previous post was to make a desktop application that really didn’t “do anything” and so it was fairly easy for me to create a .APPX package from the .EXE that it consisted of and deploy and run it.

That desktop application doesn’t depend on any specific file locations or any particular registry key and so it’s going to run ok both in a ‘regular desktop’ context and in a ‘UWP’ context.

As another, more realistic experiment, I wanted to take more of a real desktop application and see what it’s like to try and package that up into the .APPX format.

I used to build desktop applications for a living but I don’t any more and so I don’t have the source for a reasonably sized desktop application kicking around and so I had to be a little imaginative and have a think about where I might borrow a desktop application from.

Given that I use Live Writer to write my blog posts, my imagination didn’t have to wander too far to come up with the idea of using;

Open Live Writer

and so off I went to Github and cloned that repository and opened it up inside of Visual Studio “15” preview before making sure that I could actually build it and run/debug it as a regular-desktop-app which was fairly easy.

There’s a few projects in here;

Capture

and having built and run it, I then wanted to try and understand how Open Live Writer built up an installer for itself which it seems to drop out into this folder as part of the build process;

Capture

and I could see that there was the PostBuild.CreateInstaller project which has a post-build step to run a createinstaller.cmd file and the contents of that .cmd file are on the web here.

That .cmd file seems to use nuget.exe to pack a bunch of files listed in OpenLiveWriter.nuspec from the build output folder into a NuGet package.

It then uses squirrel to take that NuGet package and build the OpenLiveWriterSetup.exe from it that’s in the screenshot above.

It then wraps that up into a chocolatey package (I think Confused smile).

With those pieces, there are two (or more) ways that I could go to package this into a .APPX install.

  1. Run the Setup.exe into the desktop app converter and collect the output as per my first blog post.
  2. Add something to the build process to make a .APPX in the first place as per my second blog post.

Either way, I figured that I’d be needing a certificate so I started there.

Step 1 – Making Certificates

From my prior experiments, I know that I’m going to need a signing certificate and so I made one with;

MakeCert.exe -r -h 0 -n “CN=Open Live Writer” -eku 1.3.6.1.5.5.7.3.3 -pe -sv my.pvk my.cer

pvk2pfx.exe -pvk my.pvk -spc my.cer -pfx my.pfx

and I then installed the certificate into my local machine’s trusted roots certificate store.

I also dropped the various pieces I’d used in a top-level Certificates folder within the Open Live Writer folder structure so that I knew where to find them again.

Capture

Experiment 1 – Using the Desktop Converter

I took the OpenLiveWriterSetup.exe file that the build process emits and I ran it through the DesktopAppConverter.ps1 script with the command line below;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\apps\OpenLiveWriterSetup.exe -Version 1.0.0.0 -MakeAppx -Destination .\OpenLiveWriterOut -Publisher “CN=Open Live Writer” -PackageName “OpenLiveWriter” -Log out.txt

but I found that it seemed to run for a very long time and checking of the log file revealed that it was always waiting for the installation to complete and that never seemed to happen.

I wasn’t sure what the installer might be doing but one guess was that it might be related to the installer running the app at the end of the installation and so appearing to never end.

To see if I could mitigate that, I tried to use the “–silent” flag and in an interactive context that seemed to work (i.e. installation without app launch) but when I tagged it on to the desktop conversion process;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\OpenLiveWriterSetup.exe -Version 1.0.0.0 -MakeAppx -Destination .\OpenLiveWriterOut\ -Publisher “CN=Open Live Writer” -PackageName “OpenLiveWriter” -InstallerArguments “–silent”

I found that the tool would still sit there for a long time and a check of the log file revealed the last entry was always;

“Waiting for the installer process to complete inside Isolated Environment”

and it was tricky to know what was happening inside of that environment – was the installer trying to do something like install an earlier version of .NET? Did it relate to the installation targeting the user’s local folder rather than a central location? Was the installer showing a dialog or similar?

It’s possible that I’m just missing something around this particular type of installation but, so far, I haven’t managed to get this to work.

I moved on to the other approach.

Experiment 2 – Altering the Build Script

Manifests, Icons

I wondered if I could alter the build script that the Open Live Writer guys have built in order to package the app ‘automatically’ into a .APPX format as part of the build rather than taking the Setup.exe from the build and trying to record its output with the desktop converter tool.

I made a manifest in line with my previous post, changing a few names to ‘Open Live Writer’ where appropriate.

I’ve copied it below for completeness;

<?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="c2030cf7-74d5-479f-aa19-f907e4145698"
    ProcessorArchitecture="x64"
    Publisher="CN=Open Live Writer"
    Version="1.0.0.0" />
  <Properties>
    <DisplayName>Open Live Writer</DisplayName>
    <PublisherDisplayName>Open Live Writer</PublisherDisplayName>
    <Description>Open source version of the classic Live Writer blogging application</Description>
    <Logo>assets\StoreLogo.png</Logo>
  </Properties>
  <Resources>
    <Resource Language="en-us" />
  </Resources>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.14316.0" />
  </Dependencies>
  <Capabilities>
    <rescap:Capability Name="runFullTrust"/>
  </Capabilities>
  <Applications>
    <Application 
      Id="OpenLiveWriter" 
      Executable="OpenLiveWriter.exe" 
      EntryPoint="Windows.FullTrustApplication">
      <uap:VisualElements
       BackgroundColor="#464646"
       DisplayName="Open Live Writer"
       Square150x150Logo="assets\Square150x150Logo.png"
       Square44x44Logo="assets\Square44x44Logo.png"
       Description="Open Live Writer Blogging App" />
    </Application>
  </Applications>
</Package>

and I made logos at size 50×50, 150×50 and 44×44 based on the image that I found in OpenLiveWriter.CoreServices\Images\SplashScreenLogo.jpg although I suspect there are better logos I could have chosen here.

I added a top level folder named AppxArtefacts to hold my assets and appxmanifest.xml;

Capture

and I added the certificate pieces into a top level folder named Certificates although I really only need the .pfx file to sign the .appx as part of the build script.

I then set about seeing if I could alter the script which creates the installer.

Hacking the Build Script

I’m no expert on anything and especially not on NuGet and I’m even more clueless when it comes to Squirrel so I tried to add something into the build script that wouldn’t destroy what was already there and just move a few things around rather than dramatically change anything.

With that in mind, I added these lines to the script in between the piece which makes the NuGet package and the piece which runs Squirrel.exe on it.

:: mtaulty1 - unpack that nuget package into a folder named UnpackedNuget
"%LocalAppData%\Nuget\Nuget.exe" install -ExcludeVersion OpenLiveWriter -Source %cd% -OutputDirectory UnpackedNuget 
ECHO Unpacked that package.

:: mtaulty2 - copy the AppxArtefacts and that unpacked content into a folder named AppxContents
:: hard-coding the lib\net451 pieces here isn't great.
mkdir AppxContents
xcopy /s "AppxArtefacts\*.*" .\AppxContents
xcopy /s "UnpackedNuget\OpenLiveWriter\lib\net451\*.*" .\AppxContents

:: mtaulty3 - make an appx package out of that. Hard-coding the location of the tool here isn't great.
"c:\program files (x86)\Windows Kits\10\bin\x86\makeappx.exe" pack /d AppxContents /p OpenLiveWriter.appx

:: mtaulty4 - sign that appx package
"c:\program files (x86)\Windows Kits\10\bin\x86\signtool.exe" sign -f Certificates\my.pfx -fd SHA256 -v OpenLiveWriter.appx 

:: mtaulty5 - tidy up
rmdir /s /q AppxContents
rmdir /s /q UnpackedNuget

The attempt here is to make a .APPX file out of the pieces that are in the AppxArtefacts folder that I’ve already pre-populated mixed with the contents of the build outputs that have just been added into the NuGet package by the existing script.

Sure enough, out of this drops a .APPX file and that file install the app just fine.

Capture

Capture

Trying it Out

The app that’s installed by this process runs ‘just fine’ but it’s not entirely surprising that it then hits upon a bit of a snag…

Capture

When installed through its regular installer, this app would end up in the equivalent of c:\users\mtaulty\appdata\local\OpenLiveWriter\app-0.6.0 and when it runs up for the first time within that folder it has the permission to create a folder named plugins if it wants to.

When packaged as a .APPX, the application doesn’t have the permission to create that Plugins folder and hence it fails at startup when it tries to do it.

I wondered why the code would think it was ok to create a ‘Plugins’ folder within its own installation folder and so I dug in and saw that the code in PostEditorPluginManager.cs seems to only attempt to create this folder in the DEBUG build of the app and so maybe this is more of a developer convenience than anything else.

Repackaging the Release Code, not the Debug Code

With that in mind, I went and built the released version and tried running that up instead and that seemed to work out pretty well in the sense that it looks like I can get Open Live Writer installed and running from that .APPX package. Whether all of the functionality then works, I’m not so sure but I tried a few dialogs and it all seemed to be hanging together and so I have 2 Open Live Writers on my Start Menu;

Capture

and the ‘Windows App’ launches and does the right thing.

Wrapping Up

It’s important to say that this is (clearly) just an experiment and I didn’t worry about making the app target .NET 4.6.x and I didn’t overly worry about 32-bit/64-bit or anything like that.

I also didn’t spend too much time concerning myself over whether the app ere does/doesn’t use squirrel to do some level of automatic updating because that could clearly perhaps clash with the app having been installed via .APPX and you perhaps don’t need both the Windows Store and Squirrel both trying to keep the app automatically up to date at the same time Smile

So, to do this properly would require more thought but I wanted to experiment with it and hence the write up here and, of course, if you know why the converter tool can’t get beyond the “waiting for the installer to finish” stage then do leave me a comment and let me know.

Windows 10 Anniversary Update, Desktop App Converter Preview – Starting from Source

Following up on this post;

Initial Experiments with the “Desktop App Converter” Preview

and continuing the theme of ‘experimentation’ with the Desktop App Converter I wanted to see what it was like to start from a place where I had the source code for a desktop application and wanted to build it out into a .appx installer that could potentially go into the Windows Store.

It’s worth flagging that there are official samples that you can look at around this area and you’d find those on github here;

Desktop app bridge to UWP Samples

For my experiments, I thought I’d start small.

That’s “Hello World”-style small Smile

Making Hello World

I’m a world-class expert when it comes to building “Hello World” and so I made a new WPF application and had it show “Hello World” on the screen.

I had to dig deep but within only a few days I had this working Winking smile

Capture

and I even “Gold plated” it by adding a ViewBox around the TextBlock here Winking smile

Ok, so I’ve got a WPF “Hello World” on .NET Framework 4.6.2 and Visual Studio knows how to build a .exe from it and so how do I package that into a .appx format given that I don’t have an installer for it right now?

Making a Blank App Manifest

There’s documentation to help on this topic so I followed it;

Manually convert your Windows desktop application to a Universal Windows Platform (UWP) app

and in doing so I had to think about a few things. Firstly, I made myself a new certificate and I did that by following the docs and using the specific command;

MakeCert.exe -r -h 0 -n “CN=mikedesktop” -eku 1.3.6.1.5.5.7.3.3 -pe -sv my.pvk my.cer

pvk2pfx.exe -pvk my.pvk -spc my.cer -pfx my.pfx

and that gave me a common name of mikedesktop for the publisher here and I put that certificate into my machine’s trusted roots.

I also dug into this document that I found useful;

App packages and deployment (Windows Runtime apps)

and I also found it useful to build a Blank UWP app in Visual Studio and look at the AppxManifest.xml file that gets build as part of that process by way of having a comparison that’s in a ‘known good state’.

I added a few files to my blank WPF project as below;

Capture

I should make it clear that I’m trying to make my life easy here rather than trying to do something smart and I wrote that appxmanifest.xml file to look like this;

<?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="a2050516-ad7b-4170-ac1d-cb23a21de780"
    ProcessorArchitecture="x64"
    Publisher="CN=mikedesktop"
    Version="1.0.0.0" />
  <Properties>
    <DisplayName>Hello World Desktop App</DisplayName>
    <PublisherDisplayName>Mike Taulty</PublisherDisplayName>
    <Description>Amazing hello world desktop application</Description>
    <Logo>assets\StoreLogo.png</Logo>
  </Properties>
  <Resources>
    <Resource Language="en-us" />
  </Resources>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.14316.0" />
  </Dependencies>
  <Capabilities>
    <rescap:Capability Name="runFullTrust"/>
  </Capabilities>
  <Applications>
    <Application 
      Id="HelloWorldDesktopApp" 
      Executable="HelloWorldDesktopApp.exe" 
      EntryPoint="Windows.FullTrustApplication">
      <uap:VisualElements
       BackgroundColor="#464646"
       DisplayName="Hello World Desktop App"
       Square150x150Logo="assets\Square150x150Logo.png"
       Square44x44Logo="assets\Square44x44Logo.png"
       Description="This is my Hello World desktop app" />
    </Application>
  </Applications>
</Package>

and I made sure that the appxmanifest.xml, and the files within my assets folder all got copied to the output folder as illustrated on appxmanifest.xml below;

Capture

I should say that I copied the contents of the assets folder out of a regular, blank UWP project.

It’s also perhaps worth flagging that I set up both x86 and x64 platform targets and I worked solely within the x64 configuration as I think (i.e. not sure) that support for desktop apps packaged in .appx is 64-bit right now and I wasn’t sure how a ‘AnyCPU’ packaging would or wouldn’t work. I need to dig into that more to be sure of what does/doesn’t work.

Making a .APPX

With that set up, I went via what I thought might be the simplest route to getting a .APPX file created and I added the makeapp.exe tool to my project’s ‘post-build step’ (graphically rather than using the MSBuild AfterBuild task) in order to make that .appx;

“c:\program files (x86)\windows kits\10\bin\x86\makeappx.exe” pack /d “$(TargetDir).” /p “$(TargetDir)$(ProjectName).appx”

and that seemed to work reasonably well although it’s worth saying that I’m adding things like .pdb files into my .appx package here because they happen to be in the output folder from my build.

Signing the .APPX

I added a second post-build step to sign the .APPX using the certificate that I’d bundled into my Visual Studio project;

“c:\program files (x86)\windows kits\10\bin\x86\signtool.exe” sign -f “$(ProjectDir)Certificates\my.pfx” -fd SHA256 -v “$(TargetDir)$(ProjectName).appx”

and that also seemed to work out fine and I had a signed .APPX file.

Installing the .APPX

Once I’ve got a .APPX signed with a certificate that my machine trusts, I can simply ask the Windows shell to launch the .APPX (or use add-appxpackage) and it pops some UI;

Capture

and then it installs the app (which was too quick for me to capture as a screen shot) and then the app’s ready to go;

Capture

and runs up as you’d expect;

Capture

and that’s all fine.

Calling into UWP APIs

A desktop app can make use of some of the UWP APIs whether or not that desktop app has been packaged as a UWP app. There’s a good article here that talks about this;

UWP APIs callable from a classic desktop app

and that partitions APIs into;

  • APIs that need the calling app to have a package identity (which I read as “packaged as UWP”)
  • APIs that will work without a package identity.

beyond that, there’s a more detailed list on this page;

Supported UWP APIs for converted desktop apps

So in my desktop application I should be able to make use of an API like SpeechSynthesizer and so I thought I’d try that out and it wasn’t quite as simple as I might have hoped but it wasn’t too bad.

The steps that I took went something like this.

Edit the .csproj file to add TargetPlatformVersion

The first thing I did was to unload the project in visual studio (using the right mouse menu on the solution explorer) so that I could edit the XML and I used that to add;

Capture

and that then switches on this section of the References dialog where I added references to Windows.media and Windows.foundation;

Capture

That might work for some scenarios but I found that types like IAsyncOperation<> weren’t present and so I went out and referenced Windows.winmd instead as below;

Capture

and that reference is coming from c:\program files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd.

However, once I started writing async code, this left me without definitions of GetAwaiter() on those pesky IAsyncOperation<> types and I then need to go off and reference System.Runtime.WindowsRuntime.dll as below;

Capture

and that reference is coming from c:\program files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\4.5\System.Runtime.WindowsRuntime.dll.

I’d stress that it’s the 4.5 version I took here rather than the 4.5.1 version as referencing that seemed to give me runtime problems.

And I made sure that I set “Copy Local” to false on that assembly;

Capture

And that got me to the point where I could compile some code and so I added a button to my UI and added this code behind it;


    async void OnSpeak(object sender, RoutedEventArgs e)
    {
      // This is a UWP API.
      SpeechSynthesizer synth = new SpeechSynthesizer();

      using (var stream = await synth.SynthesizeTextToStreamAsync("I am a desktop app"))
      {
        // We need a .NET stream
        using (var netStream = stream.AsStreamForRead())
        {
          // SoundPlayer is a .NET Framework API.
          SoundPlayer player = new SoundPlayer(netStream);

          player.Play();
        }
      }
    }

and, believe it or not, that actually worked although I’m somewhat unsure whether I should Dispose() both of those stream types above or just one of them.

I’ll admit that I had to chase down quite a few web references to get to the point where that code would compile and run correctly – the text above makes it look like it would take maybe 5 minutes whereas I’d say it took about 60 minutes.

I then used my post-build steps to repackage this as a UWP once again and verified that it would deploy and work in that context as well and it worked fine.

Using the Visual Studio 15 Preview Template

Having to keep running through that process of making the desktop app, building it into a .APPX and then installing/uninstalling it is a bit painful so there’s a Visual Studio packaging project template that helps in that area. I referenced it from my earlier blog post and it’s here;

Desktop to UWP Packaging Project for Visual Studio “15”

As far as I understand it, this project template supports a particular workflow where you already have a .APPX layout for your application on disk. Maybe you made that manually as I did in this post or maybe you made it via the Desktop App Converter as I did in my previous post.

Once you’ve got it, what you can then do is;

  • Configure a list of the outputs from your project that would need to be re-deployed into that .APPX layout whenever you build your projects in Visual Studio
  • Set the packaging project as your startup
  • Press F5 and have Visual Studio build, copy over the outputs and debug your UWP packaged app in its UWP context

which takes some of the manual steps out of installing/uninstalling packages and attaching debuggers and so on.

To try this out, albeit on my simplest of examples, I first followed the steps in the docs in that I added a packaging project to my solution;

Capture

and then made sure that this project referenced my other project so that it builds after that project;

Capture

and then I revisited my other project and removed the post-build steps that I had been using to make the .APPX file and sign it.

I then went and got the .APPX file and copied it into a folder c:\temp\PackageLayout on my system where I renamed it to .ZIP and extracted the contents before getting rid of both the .ZIP and the .APPX leaving just the files and folders that were within it;

Capture

Clearly, there are some extra pieces in there like the .vshost files but that comes from me building the .APPX from the output folder of the Debug build process in the first place.

With that in place, I changed the properties of the packaging project such that the “Package Layout” option pointed to my artefacts as below;

Capture

And then, as per the instructions, I edited the AppXPackageFileList.xml which the project uses to copy files from the build output into the package layout as those files get re-built.

My file ended up looking like this;

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  
  <PropertyGroup>
    <MyProjectOutputPath>c:\temp\helloworlddesktopapp\helloworlddesktopapp\</MyProjectOutputPath>
  </PropertyGroup>
  <ItemGroup>
    <LayoutFile Include="$(MyProjectOutputPath)bin\x64\Debug\HelloWorldDesktopApp.exe">
      <PackagePath>$(PackageLayout)\HelloWorldDesktopApp.exe</PackagePath>
    </LayoutFile>
  </ItemGroup>

</Project>

and, while there’s a couple of embedded, hard-coded paths in there, it does seem to work and I can build and set this packaging project as my start up project and hitting F5 runs it up.

Capture

Whose Desktop App Is It Anyway?

I’ve now got these 2 projects with 2 views of the same thing. If I set the HelloWorldDesktopApp project to be my startup project then I run a ‘regular’ desktop app;

Capture

and if I set the PackagingProject project to be my startup then I run a ‘UWP’ desktop app;

Capture

and they look pretty similar to me and they’re running the same code and it works in both contexts so how can I be convinced that there’s anything different going on here?

I thought that perhaps I could try one of those UWP APIs that require a package identity in order to function and see what it returns in the two different contexts here and the first API that I could think of was the API that would return the current package identity.

So, I modified my code such it was trying to make use of Package.Current.Id as below;

    void OnLoaded(object sender, RoutedEventArgs e)
    {
      var packageId = "package id is not set";

      try
      {
        packageId = $"package id is {Package.Current.Id.FullName}";
      }
      catch (InvalidOperationException)
      {
      }
      this.txtPackageId.Text = packageId;
    }

and now I get 2 different results based on whether I run the HelloWorldDesktopApp project or the PackagingProject;

Capture

versus;

Capture

and that makes it clearer that these two contexts are quite different.

Initial Experiments with the “Desktop App Converter” Preview

Big Note: this post is a bunch of experimentation with the Desktop App Converter. I reference the official documents which should be your guide to what can/can’t be done and what’s supported and so on. These notes are really just the results of me experimenting with a few things as I find that’s the best/only way to learn anything and I find writing it down as I’m going along quite useful both at the time and afterwards.

Even in this modern world of mobile-first, cloud-first computing and a heterogenous client computing environment it’s fair to say that desktop applications on Windows (and on Mac) are still hugely important and billions of people are using them every day.

And, very importantly, continuing to develop them.

One of the first set of questions that I got back in 2012 when the Windows Store launched were along the lines of;

  • How do I put my existing desktop application into that Store? ( you could, but only to ‘advertise it’ )
  • How do I make use of those new WinRT APIs from my existing desktop application? ( no great answer there )
  • If I rewrite my desktop application to be ‘WinRT’ (now expanded to UWP) then what do I do about the APIs that I use which are missing? ( brokered components added something here )

These were all fair questions and, at the time, I mostly found myself having to say “not sure”.

Here in 2016, the picture is a bit clearer in that;

  1. The “Desktop App Converter” means that, with the Anniversary Update, desktop applications can truly go into the Windows Store for discovery, purchase, installation, updating, etc.
  2. Desktop applications can make use of a lot of the UWP APIs available.
  3. It’s still very fair to say that the set of APIs available to a desktop app is huge in comparison to those available to a UWP app but the UWP API set has grown a lot in the past 4 years and so includes some of those areas that developers were asking for back in 2012.

I wanted to dig into (1) and (2) above and the arrival of the Anniversary Update seemed like the perfect time to do that and that’s what this post (and maybe some follow-on-posts) is about.

If you’ve not looked at the “Desktop App Converter” then I’d suggest the //build 2016 session as a starting point.

image

and then you can go and read the docs over here;

Desktop App Converter Preview

which includes a link to go and get the preview converter tool itself.

I downloaded both the converter and the base image (3.3GB in total) and then ran through the setup instructions which all worked out fine although my machine did seem to reboot at the;

PS C:\> .\DesktopAppConverter.ps1 -Setup -BaseImage .\BaseImage-1XXXX.wim -Verbose

stage but, apart from this, it all seemed to work out.

As a note, the first time I downloaded I got presented with only a file for 14352.wim whereas I think I need a 14393.wim to work on the Anniversary Update. A revisit to the page seemed to sort that out for me so take care if you see something similar because the .wim file has to match the version of the OS you’re running on.

From here, it seems to me that there’s at least two routes that I could go with this;

  1. I have a desktop application which I build and would like to package as a .appx in order to be able to deploy that way and put in the Store etc.
  2. I have a desktop application which I have some kind of installer for and I would like to “play its installer actions” into a .appx or manually create a .appx in order to be able to deploy that way and put in the Store etc.

In the former case, a .appx file is just a fancy .zip file with some structure to it but it’s probably easier to have a tool make one if possible and Visual Studio “15” (Preview 3) does have one of those things and you can grab it from here;

Desktop to UWP Packaging Project for Visual Studio “15”

but, in the first instance I thought I’d try out the latter case and see if I could take existing installers and “play them” into a .appx using the converter.

Which desktop app should I try this out with? I tried a few.

Trial 1 – Spotify

I went to Spotify.com, downloaded their Windows installer and then wondered whether it would work or not because it’s one of those installers that goes on to download more bits from the web.

Regardless, I thought that I’d give it a whirl as everything’s a learning experience so I executed the command;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\SpotifySetup.exe –PackageName Spotify -Publisher “CN=mtaultyTest” -Version 1.0.0.0 -Destination .\SpotifyOut -MakeAppx

You can perhaps see that I have a folder for the converter, another for Apps and another for output and I’m asking the converter to make a .appx file.

Out of this popped a .appx file Smile and the tool also emitted a registry.dat, a AppxManifest.xml, some basic imagery and a VFS folder which looked like it contained the contents of all the files that the installer had tried to put down onto my system.

So far, so good! I then went through the process of signing the .appx file as described in the documentation as I can never, ever remember how to use makecert and signtool.

One thing I’d flag here – you need to make sure that the common name of your certificate here is exactly the same as the –Publisher argument that you specified to DesktopAppConverter.ps1 otherwise you’ll find that you get the “SignerSign()” error.

The other thing I’d flag is to not forget that you need to trust the certificate that you just made via makecert or the package deployment will fail.

At that point, I had a .appx file and so I went about trying to use add-appxpackage to install it and it worked.

I was actually expecting it to fail because the tool had given me a warning about not being able to figure out my app’s installation folder but, no, it worked.

pic1

followed by silence…

pic2

and then it’s installed…

pic3

and it works…

pic4

and, no, I mean it really works Smile

pic5

so that was fairly impressive.

Trial 2 – InkScape

I use InkScape quite a lot for playing with vector graphics and so I thought I’d give that a try and downloaded their 64-bit MSI and wondered whether I’d have success with it.

I was initially encouraged as the .msi has a /quiet option so I figured that might help me out and so I tried that out and hit an error;

error

Something relating to the installer returning 1639 rather than 0, 1641, 3010.

I wondered what that might mean and so I made sure that I could install the app on my system manually using the “/quiet /n” switches and that seemed to work ok.

I tried making use of the –LogFile option to see if that would help me figure out what the problem was. The log file was pretty decent but I couldn’t figure out where the 1639 was coming from based on its contents.

I wondered if the 1639 might be a red herring and so I tried the –InstallerValidExitCodes option to see what that would do for me. I was glad to see that it was an option Smile although I doubted that the installer was setting this return value for no reason.

This got me here;

Capture

Now, I actually got the W_INSTALL_PATH_NOT_DISCOVERED warning when I did Spotify so I figured that I may be able to ignore that but it seems that the W_EXE_NOT_DISCOVERED was going to cause me a problem.

I rooted around the VFS folders that the tool had made for InkScape and I couldn’t see any .exe in there myself and this was in contrast to the VFS folder that I’d seen when I’d processed Spotify.

So, for some reason the InkScape installation wasn’t working out for me here.

I wondered whether I could use a different option for the InkScape installer and so I tried the /passive flag rather than the /quiet flag. That makes the command line that I used;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\inkscape-0.91-x64.msi -PackageName InkSpace -Publisher “CN=mtaultyTest” -Version 1.0.0.0 -Destination .\InkSpaceOut -MakeAppx -InstallerArguments “/passive”

and out popped an .appx file with no warnings. I signed it. Around this time I realised that I’d called it InkSPACE rather than InkSCAPE – my mistake.

I installed it;

Capture

and I ran it…

Capture

and it seemed to work just fine Smile

Capture

I suspect that my trials/errors here were more related to me not passing the right flags to the installer rather than anything that the Desktop App Converter was doing.

Trial 3 – Paint.NET

I use Paint.NET at all the time and (as the name suggests) it’s clearly a .NET application and so I thought I’d try that out as I suspected that I might hit a snag if it isn’t currently built on .NET 4.6.1 plus I also suspected that I might have to do something to deal with the x86/x64/AnyCPU nature of the code.

I downloaded the installer for Paint.NET – unlike InkScape, it’s a .exe rather than a .msi and when you run it, it seems to extract a bunch of files somewhere.

I first off ran with this command prompt;

PS C:\temp\dac> .\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\paint.net.4.0.10.install.exe -PackageName PaintNet -Version 1.0.0.0 -Publisher “CN=mtaultyTest” -MakeAppx -InstallerArguments “/skipConfig” -Destination ./PaintNetOut

and I hit the same snag that I’d hit when trying out InkScape – the two warnings around W_EXE_NOT_DISCOVERED and W_INSTALL_PATH_NOT_DISCOVERED leading to the error E_CANNOT_MAKEAPPX_WITHOUT_EXE which seems entirely reasonable.

I wondered whether there was a tweak that I could make to the parameters again and I tried the “/auto” option to the installer but got the same result.

It felt like the Paint.NET installer wasn’t installing itself into the containerised environment.

However, the installer does have an option /createMsi which makes a .MSI for Paint.NET so I wondered whether that might give different results and I asked it to make that .MSI (it makes both an x86 and x64 variant) and then took the 64-bit version and tried to see if I could use the Desktop App Converter to make a .appx from that.

If I ran the .MSI that I’d created interactively then it worked fine and Paint.NET got installed whereas, typically, different combinations of parameters to both the Desktop App Converter and the Paint.NET .MSI seemed to cause the Desktop App Converter to error in different ways but the error generally related to either;

  • The .MSI has exited with a code other than 0. Now, I could try and hack around this by telling the converter that the error code was “ok to ignore” but that didn’t help because I’d then get…
  • The converter can’t find the EXE inside of the bits that the .MSI has installed.

And I’d find that if I dug around in the VFS folder that the converter had created then I couldn’t find the .EXE file either so I could understand why the converter was having trouble but why was the installer not installing the .EXE file in the first place?

This had me poking around in the log file that the Desktop App Converter produces and it’s pretty decent with the revelant bit for me here seeming to be;

Running Installer in Isolated Environment

[2016-08-05T11:10:42] No installer valid exit codes specified. Using defaults: 0 1641 3010
[2016-08-05T11:10:42] Replace any occurence of <log_folder> in -InstallerArguments with C:\shared\logs
[2016-08-05T11:10:42] Creating Isolated Environment
[2016-08-05T11:10:43] Starting the Isolated Environment
[2016-08-05T11:10:45] Logging known folder locations in Isolated Environment
[2016-08-05T11:10:45] Command line: “C:\shared\dac\bin\KnownFolderDump.exe” “C:\shared\dac\out\KnownFolders.xml”
[2016-08-05T11:10:45] Waiting for known folder mapping process to finish in Isolated Environment
[2016-08-05T11:10:56] Running installer command in Isolated Environment
[2016-08-05T11:10:56] Command line: “C:\WINDOWS\system32\msiexec.exe” /quiet DISABLEADVTSHORTCUTS=1 INSTALLSTARTMENUSHORTCUTS=1 /norestart /i “C:\shared\installer\PaintDotNet_x64.msi” /l*v “C:\shared\logs\install_PaintDotNet_x64.log”
[2016-08-05T11:10:56] Waiting for installer process to complete inside Isolated Environment
[2016-08-05T11:11:41] Stopping the Isolated Environment
[2016-08-05T11:11:42] Exporting changes made by your installer inside the Isolated Environment
[2016-08-05T11:11:48] Disposing of the Isolated Environment
[2016-08-05T11:11:48] Moving any logs in C:\DesktopAppConverter\021d9821-ef35-4641-95dd-0f3f4c1411ed\shared\logs to .
[2016-08-05T11:11:48] Moving any dac output in C:\DesktopAppConverter\021d9821-ef35-4641-95dd-0f3f4c1411ed\shared\dac\out to C:\DesktopAppConverter\021d9821-ef35-4641-95dd-0f3f4c1411ed\output\dac
[2016-08-05T11:11:48] Cleaning up by removing isolated environment shared folder C:\DesktopAppConverter\021d9821-ef35-4641-95dd-0f3f4c1411ed\shared
[2016-08-05T11:11:48] Isolated Install Complete. Moving output from C:\DesktopAppConverter\021d9821-ef35-4641-95dd-0f3f4c1411ed\output\export to C:\DesktopAppConverter\021d9821-ef35-4641-95dd-0f3f4c1411ed\output\packageRoot
[2016-08-05T11:11:48] Checking installer exit code 0 to ensure it is indicative of success
[2016-08-05T11:11:48] Isolated Installer Run Complete!
[2016-08-05T11:11:48] ————————————————–

and that all looked pretty good and I can see that it copies the log that the installer makes out of the isolated environment for me so I had a look in there and there was something that caught my eye amongst all of the spew which was as far as I could tell (and I’m not good at reading .MSI logs) the installer did seem to be laying down the .EXE file;

MSI (s) (28:20) [11:10:59:161]: Executing op: FileCopy(SourceName=PAINTD~1.EXE|PaintDotNet.exe,SourceCabKey=_BC1135F9DC4A8788EA40968E8DE47726,DestName=PaintDotNet.exe,Attributes=512,FileSize=1764048,PerTick=65536,,VerifyMedia=1,,,,,CheckCRC=0,Version=4.10.6033.29846,Language=0,InstallMode=58982400,,,,,,,)
MSI (s) (28:20) [11:10:59:161]: File: C:\Program Files\paint.net\PaintDotNet.exe;    To be installed;    Won’t patch;    No existing file

and it seemed to be putting it into C:\Program Files\paint.net which didn’t seem to be unreasonable but I wondered about this installation into c:\program files\ as there’s a comment in the Desktop App Converter help page which sounds ‘similar’ if not the same to this;

“A .NET app compiled with “AnyCPU” build option will fail to install if the main executable or any of the dependencies were placed under “Program Files” or “Windows\System32”. As a workaround, please use your architecture specific desktop installer (32 bit or 64 bit) to successfully generate an AppX package.”

Whether this was my issue or not, I wasn’t sure but I thought I’d see if I could alter that behaviour and see if it made any difference and so I first asked the Paint.NET installer to make me an .MSI;

.\paint.net.4.0.10.install.exe /createMsi /auto /skipConfig TARGETDIR=”c:\paintnet”

and I was hoping that this .MSI would install into c:\paintnet\ rather than Program Files. I then took the 64-bit .MSI package that made and ran it through the converter;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\PaintDotNet_x64.msi -Publisher “CN=mtaultyTest” -Version 1.0.0.0 -Destination .\PaintNetOut -MakeAppx -PackageName PaintNet -InstallerArguments “/passive” -LogFile .\log.txt

and that produced me a .appx file albeit once again with a warning around finding binaries. I then signed that .appx as before and installed it.

Capture

and, ran it;

Capture

and, sure enough, there’s Paint.NET;

Capture

Now, I must admit that I’m pretty sure that Paint.NET does some NGEN’ing as part of its initial installation and I’m very unsure whether this code that I’m running here as been NGEN’d or not – I suspect not but I haven’t dug in and tried to figure that out.

But, after quite a bit of fumbling (and some learning around where log files go and what they look like) I got it working Smile

Trial 4 – Skype

On the Windows 10 Anniversary Update, there’s a preview of a UWP version of Skype already installed which I really like the look of but I wondered if I could get the Skype desktop offline installer to package itself up as a .appx and install that desktop application that way because I still use that desktop Skype app on my main Surface Pro 3 system today (running 1511).

I went off to Skype and got their installer;

Skype Full Installer

From a bit of web searching I found that this installer supports arguments /VERYSILENT and /NOLAUNCH and so I wanted to see if I could pass them to the app converter. My initial attempt produced this;

PS C:\temp\DAC> .\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\skypesetupfull.exe -Publisher “CN=mtaultyTest” -Version 1.0.0.0 -Destination .\SkypeOut -MakeAppx -PackageName SkypeDesktop -InstallerArguments “/VERYSILENT”, “/NOLAUNCH”, “/SP-“,”/SUPPRESSMSGBOXES”,”/NOCANCEL”,”/NORESTART” -LogFile .\log.txt

WARNING: DesktopAppConverter : warning ‘W_DISCOVERED_EXE_NOT_FOUND’: Discovered shortcut indicates the app executable
should be ‘\WINDOWS\Installer\{FC965A47-4839-40CA-B618-18F486F042C6}\SkypeIcon.exe’, but this file cannot be found.
MakeAppx will fail until you fix the Application Executable property in the AppxManifest.xml.

and so it feels like the converter is assuming that SkypeIcon.exe is the application’s executable here but I don’t think it is. I think that should, perhaps, be Skype.exe and I poked around the outputted folder structure within the VFS directory and could see that this seems to end up in ProgramFilesX86\Skype\Phone\Skype.exe.

So…maybe the .EXE is there but the converter isn’t finding it and there’s the –AppExectuable parameter which, I think, tells the converter to stop trying to work this out for itself. So, I tried that;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\skypesetupfull.exe -Publisher “CN=mtaultyTest” -Version 1.0.0.0 -Destination .\SkypeOut -MakeAppx -PackageName SkypeDesktop -InstallerArguments “/VERYSILENT”, “/NOLAUNCH”, “/SP-“,”/SUPPRESSMSGBOXES”,”/NOCANCEL”,”/NORESTART” -LogFile .\log.txt -AppExecutable “c:\program files (x86)\Skype\Phone\Skype.exe”

and I can’t say that every one of those flags to the Skype installer is required as such but it worked out and I got an .appx out of it and got past my error.

I signed the .appx and installed Skype from the .appx package but I realised that my victory here had been short-lived as I saw;

Capture

So I think I’d need to revisit this one and try a bit harder if I wanted to get this one to work fully.

Trial 5 – VLC

I use VLC quite a bit and so I went and grabbed their installer;

VLC Installer

which seemed to support a /S parameter for silent mode. I ran that first, interactively, on my system to see if it worked and it did seem so and so I uninstalled it and then set about running that installer through the app converter;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\vlc-2.2.4-win32.exe -Publisher “CN=mtaultyTest” -Version 1.0.0.0 -Destination .\VLCOut -MakeAppx -PackageName VLC -InstallerArguments “/S” -LogFile .\log.txt

and that all worked out fine as far as I could tell. However, when I ran the signtool on the resulting .appx file I got an error which I checked by setting the APPXSIP_LOG environment variable to 1 and then re-running;

C:\temp\DAC\VLCOut>signtool sign -f ..\my.pfx -fd SHA256 -v .\VLC.appx
The following certificate was selected:
    Issued to: mtaultyTest
    Issued by: mtaultyTest
    Expires:   Sat Dec 31 23:59:59 2039
    SHA1 hash: AB1863ED8ABB6280978B9D96A4690B8E50DC5634

Done Adding Additional Store
ERROR: [AppxSipCustomLoggerCallback] File has malformed certificate: VLC\uninstall.exe

So, I get a malformed certificate on this uninstall.exe which, ironically, I guess doesn’t really need to be there given that installation is now handled by .appx.

I figured that I could maybe address this for this particular instance by taking off the –MakeAppx parameter and re-running the command to make the output folder without the .appx file.

Then, I deleted the offending uninstall.exe so that it wouldn’t be packaged into the .appx before running;

MakeApp.exe /d VLCOut /p VLC.appx

to make the .appx myself and then signing it with the signing tool.

That seemed to work ok and so I ran the .appx file;

Capture

and let the app install and in so much as I could tell I got VLC installed;

Capture

Final Scores

Ok, from my initial experiments here it looks like it’s a 4-1 win to me in the sense that I got 4 out of 5 of these installers to play nice with me and the desktop app converter and I learned quite a lot along the way about the process and the sorts of things that I might look for in taking an installer like this and trying to convert it to .appx format.

There’s a lot more to learn but it’s a start Smile