Free MSIX Packaging Tool for the Package Support Framework

This post provides a free MSIX Packaging tool for the Package Support Framework (PSF), and is hosted in the PowerShell Gallery.

Most of us already know that native MSIX has its limitations (at time of writing) in terms of compatibility with some applications. For example, once we package an application into MSIX format we may find that it has issues finding DLLs, or generates Access Denied warnings amongst other annoying things.

So the folks over at Microsoft created the Package Support Framework (PSF), which was later forked by the great Tim Mangan to include additional functionality. The PSF essentially comprises of an executable which acts as the “PSF launcher”, a bunch of associated DLLs that provide “fix-ups”, and a config.json file which tells the launcher what to fix!

How Does The Package Support Framework Work?

In simple terms, we redirect any problematic shortcuts to the PSF launcher instead of the original executable. The PSF launcher then reads the associated config.json file which tells it what to do in terms of which executable to launch (the original executable) and which fix-ups to apply at runtime.

Why AlkanePSF?

When I originally wrote this (over a year ago), injecting the PSF into an MSIX package manually was a bit laborious and fiddly to say the least. The official MSIX packaging tool GUI didn’t support PSF configurations at the time (if I recall), and amending JSON files is prone to human error. So I created this PowerShell module called AlkanePSF that makes life easy (or easier).

The advantages of this scripted approach are that:

  • It doesn’t require the MSIX packager. You can extract, inject fix-ups, recompile and sign on-the-fly.
  • It could be used as part of an automation pipeline to inject fixes into a bunch of packages on a file share, or perhaps at the end of an automated conversion batch.
  • It supports Tim Mangan’s PSF (which includes fix-ups like the MFR fix-up), which the official MSIX packaging tool doesn’t natively support.
  • It’s fast when making fix-up modifications and re-retesting packages.

Caveats

  • Whilst it does support every fix-up type, it doesn’t support every single fix-up configuration.

Assumptions

  • You’ve already packaged your MSIX, launched it and it doesn’t work.
  • You might have run some process monitoring, and have an understanding of which fixups you might need to apply. Maybe read the Package Support Framework Overview first.
  • You have a code signing certificate and a password.
  • You have a basic understanding of fix-ups. If not, use the links below to understand the different types.

Available Cmdlets

Install-AlkanePSFPrerequisite – Installs Windows SDK/Microsoft PSF/Tim Mangan PSF.

New-AlkanePSFStagedPackage – Extracts/stages MSIX.

Add-AlkanePSFApplication – Adds an application from the AppxManifest.xml to “fix”. This must be called for any application that requires fixing.

Add-AlkanePSFFileRedirectionFixup – Adds a FileRedirectionFixup to the application.

Add-AlkanePSFRegLegacyFixup – Adds a RegLegacyFixup to the application.

Add-AlkanePSFEnvVarFixup – Adds a EnvVarFixup to the application.

Add-AlkanePSFDynamicLibraryFixup – Adds a DynamicLibraryFixup to the application.

Add-AlkanePSFMFRFixup – Adds a MFRFixup to the application (Tim Mangan only).

Add-AlkanePSFTraceFixup – Adds support for the trace module.

Add-AlkanePSFStartScript – Adds a StartScript.

Add-AlkanePSFEndScript – Adds an EndScript.

Set-AlkanePSF – Updates AppxManifest.xml, generates config.json.

New-AlkanePSFMSIX – Compiles and signs new MSIX.

Usage of Free MSIX Packaging Tool for the Package Support Framework

One day I’ll get around to documenting it properly. But until that day comes, there are three things to do:

  1. Copy the script below and paste it into a PowerShell ISE session.
  2. Update the variables inside the TODO1 section
  3. Add your own fixups – I’ve added lots of examples below, so remove what you don’t require and modify the rest.
cls
#********************************************
#********************************************
#START TODO1 : Define variables
#********************************************
#********************************************
#The version of AlkanePSF - always check for the latest version here: https://www.powershellgallery.com/packages/AlkanePSF/
$alkanePSFVersion = "2.0.8"
#Define path to input MSIX that needs fixing
$alkanePathToMSIX ="C:\Alkane\alkane.msix"
#Define path to output MSIX
$alkanePathToFixedMSIX ="C:\Alkane\alkane-fixed.msix"
#Define application architecture (32/64).  This will define which PSF DLLs to copy.
$alkaneAppArchitecture = "32" 
#Define path to code signing certificate
$alkanePathToCertificate  = "C:\Alkane\alkanecertificate.pfx"  
#Define password for code signing certificate (if your password contains $ you'll need to prefix with a backtick `$)   
$alkaneCertificatePassword  = "alkanepassword"  
#Specify the name of Tim Mangan's zip release, listed here: https://github.com/TimMangan/MSIX-PackageSupportFramework
$alkaneTmZipName = "ZipRelease.zip-v2024-1-2.zip"
#********************************************
#********************************************
#END TODO1 : Define variables
#********************************************
#********************************************
#Checking if we need to install module
$foundModule = $false
get-module -Name AlkanePSF | foreach {
$version = [string]$_.Version   
if ($version -ne $alkanePSFVersion) {
write-host "Removing AlkanePSF version $version" 
$_ | Remove-Module -Force
} else {
$foundModule = $true
}
}
if (!($foundModule)) {
write-host "Installing AlkanePSF version $alkanePSFVersion"
Install-Module AlkanePSF -RequiredVersion $alkanePSFVersion -Force
}
#Import module
Import-Module AlkanePSF -RequiredVersion $alkanePSFVersion -Force
#Install prerequisites such as Windows SDK, MS PSF, TM PSF
Install-AlkanePSFPrerequisite -ForceReinstall $false
#Stage and extract our MSIX package so we can apply fixups
New-AlkanePSFStagedPackage
#********************************************
#********************************************
#START TODO2 : Define Fixups
#********************************************
#********************************************
#Add first app for PSF fix with working directory fixup and inPackageContext fixup
Add-AlkanePSFApplication -FixupApplicationId "SAMPLEAPP" -FixupWorkingDirectory "VFS\ProgramFilesX86\Alkane\" -FixupArguments @("-exampleargument") -FixupInPackageContext $true
Add-AlkanePSFTraceFixup -FixupApplicationId "SAMPLEAPP" -FixupTraceMethod "outputDebugString" -FixupWaitForDebugger $false -FixupTraceFunctionEntry $false -FixupTraceCallingModule $true -FixupIgnoreDllLoad $true -FixupTraceLevelProperty "default" -FixupTraceLevelValue "allFailures" -FixupBreakOnProperty "default" -FixupBreakOnValue "allFailures"
#Add env var fixups
Add-AlkanePSFEnvVarFixup -FixupApplicationId "SAMPLEAPP" -FixupVarName "alkaneenv1" -FixupVarValue "alkanevalue1" -FixupVarUseRegistry $true
Add-AlkanePSFEnvVarFixup -FixupApplicationId "SAMPLEAPP" -FixupVarName "alkaneenv2" -FixupVarValue "alkanevalue2" -FixupVarUseRegistry $true
#Add DLL fixups
Add-AlkanePSFDynamicLibraryFixup -FixupApplicationId "SAMPLEAPP" -FixupDllName "alkane1.dll" -FixupDllFilepath "VFS\Alkane\alkane1.dll"
Add-AlkanePSFDynamicLibraryFixup -FixupApplicationId "SAMPLEAPP" -FixupDllName "alkane2.dll" -FixupDllFilepath "VFS\Alkane\alkane2.dll"
#Add reg legacy fixups
Add-AlkanePSFRegLegacyFixup -FixupApplicationId "SAMPLEAPP" -FixupType "FakeDelete" -FixupHive "HKCU" -FixupAccess "FULL2RW" -FixupPatterns@(".+\.exe",".+\.dll")
Add-AlkanePSFRegLegacyFixup -FixupApplicationId "SAMPLEAPP" -FixupType "ModifyKeyAccess" -FixupHive "HKLM" -FixupAccess "Full2MaxAllowed" -FixupPatterns@(".+\.exe",".+\.dll")
#Add MFR fixups (only applies to Tim Mangan PSF)
Add-AlkanePSFMFRFixup -FixupApplicationId "SAMPLEAPP" -FixupType "overrideLocalRedirections" -FixupName "ThisPCDesktopFolder" -FixupMode "Disabled"
Add-AlkanePSFMFRFixup -FixupApplicationId "SAMPLEAPP" -FixupType "overrideLocalRedirections" -FixupName "Personal" -FixupMode "traditional"
#Add file redirection fixups
Add-AlkanePSFFileRedirectionFixup -FixupApplicationId "SAMPLEAPP" -FixupType "packageDriveRelative" -FixupBase "example3" -FixupPatterns @(".+\.log")
Add-AlkanePSFFileRedirectionFixup -FixupApplicationId "SAMPLEAPP" -FixupType "knownFolders" -FixupId "ProgramFilesX64" -FixupBase "example1" -FixupPatterns @(".+\.log")
Add-AlkanePSFFileRedirectionFixup -FixupApplicationId "SAMPLEAPP" -FixupType "packageRelative" -FixupBase "example2" -FixupPatterns @(".+\.log")
Add-AlkanePSFFileRedirectionFixup -FixupApplicationId "SAMPLEAPP" -FixupType "knownFolders" -FixupId "ProgramFilesX64" -FixupBase "example4" -FixupPatterns @(".+\.log")
Add-AlkanePSFFileRedirectionFixup -FixupApplicationId "SAMPLEAPP" -FixupType "packageDriveRelative" -FixupBase "example3" -FixupPatterns @(".+\.log")
#An example of applying dynamic file redirection fixups
$workFolder = (get-item $alkanePathToMSIX).Directory.FullName
$packageName = (get-item $alkanePathToMSIX).Basename
$stagingFolder    = "$workFolder\$($packageName)_Staged" 
$vfsFolder    = "$stagingFolder\VFS"
#find unique directories containing log files
Get-ChildItem -Path $vfsFolder -Recurse  -Include *.log | Select -ExpandProperty DirectoryName -Unique | foreach {
$fullPath = $_.Replace($vfsFolder,"VFS")
Add-AlkanePSFFileRedirectionFixup -FixupApplicationId "SAMPLEAPP" -FixupType "packageDriveRelative" -FixupBase $fullPath -FixupPatterns @(".+\.log")
}
#add scripts to run at start and end
Add-AlkanePSFStartScript -FixupApplicationId "SAMPLEAPP" -FixupScriptPath "VFS\ProgramFilesX86\Alkane\example.exe" -FixupScriptArguments @("-install","-allusers") -FixupRunInVirtualEnvironment $true -FixupShowWindow $true -FixupStopOnScriptError $false -FixupWaitForScriptToFinish $true -FixupTimeout 10
Add-AlkanePSFEndScript -FixupApplicationId "SAMPLEAPP" -FixupScriptPath "VFS\ProgramFilesX86\Alkane\example.exe" -FixupScriptArguments @("-install") -FixupRunInVirtualEnvironment $true -FixupShowWindow $true -FixupStopOnScriptError $false -FixupWaitForScriptToFinish $true -FixupTimeout 10
#********************************************
#********************************************
#END TODO2 : Define Fixups
#********************************************
#********************************************
#update AppxManifest.xml, generate config.json
Set-AlkanePSF -FixupPSFType "TM" -FixupOpenConfigJson $false
#Compile staged files into MSIX and sign
New-AlkanePSFMSIX

You can quickly tweak and regenerate your MSIX file to test modifications in a matter of seconds – just re-run the script!

Hint: If you want to view the config.json at the end of each compilation, set -FixupOpenConfigJson $true .

This is work in progress, so your feedback in the comments below is always welcome!

Installing the Package Support Framework (PSF) Using PowerShell

This blog provides an example of installing the Package Support Framework (PSF) using PowerShell.

We’ve discussed previously what the Package Support Framework (PSF) for MSIX is, and how we can use it to create fixups for MSIX packages. We also outlined how to manually obtain the PSF files required to fix our MSIX applications. But this post provides a more automated approach of installing PSF for MSIX:

Write-Host "Installing PSF..."
$nuget = get-packagesource | Where-Object ProviderName -eq "Nuget"
if ($nuget -eq $null) {
Register-PackageSource -Name nuget.org -Location https://www.nuget.org/api/v2 -ProviderName NuGet
Install-Package -Name Microsoft.PackageSupportFramework -ProviderName Nuget -Force
} else {
$package = Get-Package | Where-Object Name -eq "Microsoft.PackageSupportFramework"
if ($nuget -eq $null) {
Install-Package -Name Microsoft.PackageSupportFramework -ProviderName Nuget -Force
}
}
$nupkg = Get-Package | Where-Object Name -eq "Microsoft.PackageSupportFramework" | Select -ExpandProperty Source
$psfBinFolder = (get-item $nupkg).Directory.FullName + "\bin\"
write-host "The PSF bin folder is $psfBinFolder"
Write-host "Finished"

MSIX Shortcut Arguments/Parameters

MSIX doesn’t natively support shortcut arguments. This blog explains how we can use the Package Support Framework (PSF) to add MSIX shortcut arguments/parameters.

Install the Package Support Framework (PSF)

There are two options to install the PSF for MSIX.

Option 1 – Manually Download PSF for MSIX

  • Navigate to the Package Support Framework page on Nuget: https://www.nuget.org/packages/Microsoft.PackageSupportFramework/
  • Click Download Package and you will download a file called something similar to: microsoft.packagesupportframework.[version].nupkg
  • Rename the extension from nupkg to zip, and extract it.
  • Navigate to the bin folder and copy all the contents somewhere local. These files form the Package Support Framework and we’ll need some of them later on.

Package Support Framework

Option 2 – Install PSF for MSIX using Powershell

Follow our post on how to install the Package Support Framework for MSIX using PowerShell.

Add Shortcut Arguments/Parameters To Your MSIX

      • Open your MSIX in edit mode using the MSIX Packaging Tool.
      • Navigate to the Package Information tab and scroll down to the Manifest File section. Click Open File, and it will open the AppxManifest.xml file for your package.
      • In the AppxManifest.xml file will be a node called Applications , and this may contain one or many sub-nodes called Application . This represents all the applications in your package and will look similar to this:
        <Applications>
        <Application Id="ALKANEAPP" Executable="VFS\ProgramFilesX86\Alkane\Alkane.exe" EntryPoint="Windows.FullTrustApplication">
        <uap:VisualElements BackgroundColor="transparent" DisplayName="Alkane Solutions" Square150x150Logo="Assets\ALKANEAPP-Square150x150Logo.png" Square44x44Logo="Assets\ALKANEAPP-Square44x44Logo.png" Description="Alkane Solutions">
        <uap:DefaultTile Wide310x150Logo="Assets\ALKANEAPP-Wide310x150Logo.png" Square310x310Logo="Assets\ALKANEAPP-Square310x310Logo.png" Square71x71Logo="Assets\ALKANEAPP-Square71x71Logo.png" />
        </uap:VisualElements>
        <Extensions>
        <desktop7:Extension Category="windows.shortcut">
        <desktop7:Shortcut File="[{Programs}]\Alkane\Alkane Solutions.lnk" Icon="VFS\ProgramFilesX86\Alkane\App.exe" />
        </desktop7:Extension>
        </Extensions>
        </Application>
        </Applications>
      • Make a note of the Application Id ALKANEAPP and also the Executable value of VFS\ProgramFilesX86\Alkane\Alkane.exe because we’ll need these values later.
      • Change the Executable value to be PsfLauncher32.exe . We are now telling the package that when the shortcut is clicked we want to launch PsfLauncher32.exe and NOT Alkane.exe . The second line should now look like this:
        <Application Id="ALKANEAPP" Executable="PsfLauncher32.exe" EntryPoint="Windows.FullTrustApplication">
      • Now save the file and close it.
      • We now need to add the PSF files to our package. Navigate to the Package Files tab, right click on the Package folder and click Add file….
      • From your downloaded Package Support Framework (PSF) select the following 6 files (hold down ctrl to select them all) and add them to your package (we don’t need the others, which are related to PSF ‘fixups’):
        • PsfLauncher32.exe
        • PsfRuntime32.dll
        • PsfRunDll32.exe
        • PsfLauncher64.exe
        • PsfRuntime64.dll
        • PsfRunDll64.exe

        Note – we really only need to add the files for our application architecture (NOT our operating system architecture). So if our application was 32-bit and our operating system was 64-bit, we would just add the 3 files containing ’32’.

      • Now we know our package is opening PsfLauncher32.exe, but it doesn’t know what to do with it! So we need to create a file called config.json to tell our package what to do.
      • Open notepad (or Notepad++) and create/save a file called config.json with the following content:
        {
        "applications": [
        {
        "id": "ALKANEAPP",
        "executable": "VFS\\ProgramFilesX86\\Alkane\\Alkane.exe", 
        "arguments": "-alkaneargument" 
        }] 
        }
      • You will notice that we have referenced the application ID in the AppxManifest.xml (ALKANEAPP), and we have specified the executable to be the original executable in the AppxManifest.xml (VFS\\ProgramFilesX86\\Alkane\\Alkane.exe). You will also notice that for valid JSON we either need to escape backslashes with another backslash (\\) or we need to replace them with a forward slash. Finally you will notice that we are passing in an argument of -alkaneargument .
      • We now need to add the config.json to our package. Navigate to the Package Files tab, right click on the Package folder and click Add file….
      • Add the config.json file, which should sit alongside the PSF files you added earlier.
      • Save your package and test it.

We have essentially told our MSIX shortcut to launch PsfLauncher32.exe from the shortcut. PsfLauncher32.exe will then read the config.json file and subsequently launch VFS\\ProgramFilesX86\\Alkane\\Alkane.exe with an argument of -alkaneargument.

What is the Package Support Framework (PSF) for MSIX?

MSIX application compatibility can be improved using the Package Support Framework. But what is the Package Support Framework (PSF) for MSIX?

The Package Support Framework (PSF) is an open-source framework that allows developers to run their traditional (Win32) applications inside MSIX containers with the same level of isolation and security as native UWP applications. The PSF achieves this by providing a set of runtime libraries and a wrapper that runs the application inside the container.

The PSF is designed to address some of the challenges of modernizing legacy Windows applications to work with the MSIX format. These challenges include dealing with dependencies on legacy technologies and interfaces, managing compatibility issues with different versions of Windows, and providing a secure, isolated environment for the application.

The PSF can be used to package and deploy legacy Windows applications in the MSIX format without requiring code changes to the application itself. It includes features such as file and registry redirection, manifest injection, and custom actions that allow developers to customize the runtime behavior of their applications.

The PSF is available as an open-source project on GitHub and can be used with any MSIX package. It is actively maintained and updated by the community, and provides a valuable tool for developers looking to modernize their legacy Windows applications.

What Does The Package Support Framework (PSF) Consists Of?

The Package Support Framework (PSF) consists of two main components: the PSF Runtime and the PSF Launcher.

  1. PSF Runtime: The PSF Runtime is a set of DLLs that provide runtime services to the application running inside the MSIX container. These DLLs are injected into the application process at runtime and provide features such as file and registry redirection, manifest injection, and custom actions. The PSF Runtime is responsible for intercepting and redirecting API calls made by the application to the underlying operating system, allowing it to run in a secure, isolated environment.
  2. PSF Launcher: The PSF Launcher is a small executable that is responsible for launching the application inside the MSIX container. It is responsible for setting up the environment for the application, including setting up the PSF Runtime, configuring the container, and launching the application process. The PSF Launcher can be customized by developers to add custom actions or configuration options.

In addition to these main components, the PSF also includes a set of configuration files that can be used to customize the behaviour of the PSF Runtime and Launcher. One of these files is config.json, which contains configuration options for the PSF Launcher, including the location of the PSF Runtime DLLs and any custom actions.

The PSF is designed to be highly customizable and extensible, allowing developers to tailor its behavior to meet the specific needs of their applications. Its modular architecture and open-source nature make it a valuable tool for modernizing legacy Windows applications and deploying them in the MSIX format.