advanced MSI packaging

YOU’RE READING

MSIX Packaging Fundamentals

by Tim Mangan, Bogdan Mitrache & Kevin Kaminski

Download ebook

Package Support Framework

Running Win32/WPF applications inside a MSIX container is a different paradigm. While there are benefits, the container also can cause issues with applications when you simply repackage them without making changes to the application source.

To help with the MSIX transition, Microsoft has created the Package Support Framework (PSF) -- an open-source framework that enables you to apply runtime fixups for issues that might appear when migrating legacy applications.

Conceptually, the PSF works using fixups that are injected into applications to modify the application behavior, as shown in the following diagram:

Integrating the Package Support Framework

Inside the Package Support Framework (PSF)

The official Microsoft release for the Package Support Framework (PSF) is available on GitHub - MSIX Package Support Framework.

Although Microsoft created the PSF, they wanted the community to support and add to it. For various reasons, a separate branch has evolved that contains the latest version. This may be found at https://github.com/TimMangan/MSIX-PackageSupportFramework. This branch also has a Wiki which was written specifically for IT personnel wanting to learn more about the PSF (Microsoft documentation is more developer focussed).

The Microsoft MSIX Packaging Tool uses the Microsoft fork, which is missing upwards of 3 years of changes at this point. The TimMangan fork is known to be used not only in TMurgent software, but also used by other vendors that provide PSF support. AdvancedInstaller is known to use their own repository, which has been taken from both of these sources and enhanced with some of their own proprietary changes.

Under the hood, the Package Support Framework uses the Detours technology, an open-source framework for handling API redirection and hooking. Aside from allowing you to move forward in instances when you can't access the source code, this technology will also help make your application smarter.

With this framework, you can either choose to use predefined fixups or create a new fixup yourself. Since creating custom fixups is more of a developer matter, we will limit our focus on how to use the predefined fixups.

To start using PSF inside your MSIX package, you don’t need the entire software that is in the GitHub repository for the PSF, as this includes additional content to help with testing the PSF itself.

These are the main resources that must be included into your MSIX package in order to use the PSF:

  • PsfLauncher - Necessary to use the PSF.
  • The PSF runtimes - these should be included depending on your application’s architecture
  • A config JSON - specifies and configures the fixups used by your application
  • The fixups DLL(s) - predefined or any custom ones you might build

Commercial packaging tools like Advanced Installer, InstallShield, and RayPack provide direct integration with the Package Support Framework and take care of including the binaries listed above semi-automatically (you still need to perform configurations in their GUIs).

Once you have defined the traditional app ingredients, either by a recapture or by building an installation ingredient list, you would then add the appropriate PSF file to the package, followed by changing out a shortcut, and adding a configuration file that tells PSF what to do. Most of the tools mentioned above do this automatically.

Using the PsfLauncher

PsfLaunher is the work-horse of the PSF, and is needed to be able to use the capabilities of the Package Support Framework.

In essence, you will replace the traditional shortcut .ink file added by the application installer with a new one. The old shortcut pointed to a target executable (possibly with command-line arguments, a working directory, and an icon). Your new shortcut will replace the target executable with a copy of the PSF launcher executable placed in your package.

This PSF launcher can have any name you want. For example, the PsfTooling kit injects a file named like PsfLauncher1.exe while Advanced Installer injects a file called AiStub.exe. Sometimes, we need to use alternate when a package contains more than one shortcut as MSIX restricts any individual package from having two shortcuts to the same target file.

The configuration file (config.json) is used to inform PsfLauncher what the real target application is and how to start it. The launcher starts the real target EXE and performs DLL injection for a fixup to be applied. This config file specifies what fixups should be injected into which target processes and also provides the behavioral configuration needed for the specific fixup. A utility DLL called “PsfRuntime” is used by both the launcher and injected into the target to make all of this work.

The startup sequence for this is depicted in the image below, where FileRedirectionFixup.dll is injected into the target executable process. For example, when an application is trying to write in its installation folder (an action forbidden by the MSIX standard), the runtime fixups redirect the call to a new location under AppData.

How Does Package Support Framework Work

In addition to launching the target application, the PsfLauncher may be configured in the Json to support command line arguments, the working directory, scripts, and PsfMonitor.

How to determine the needed fixups

The most difficult part of using the PSF is knowing when and what you need it for. Determining the fixups that your application requires can be a very time-consuming process.

Some of the third-party vendors include capabilities in their tooling to provide analysis of the traditional package before you start packaging for MSIX. These vendors can figure out what is needed for most apps, but none of the available tools are perfect at this for every app.

This means that you will have to invest in your own efforts to track your app’s compatibility issues. And for the most part, that means creating packages and testing.

To help with that, you have a few options commonly used:

1. Use the PSF tracing dll, PsfTraceFixup, inside your package to show what are the Windows API calls that your application makes.

1.1 This special fixup traces points that you might need to modify in application activities by using the other fixups.

1.2 The fixup supports two output forms.

  • One is the output to the debug console port -- and you need to use a tool like DebugView to capture and view the output.
  • The other form is to an event log -- and you need to use PsfMonitor (also part of the PSF) to capture and view the output.
  • These logs are also automatically parsed by Advanced Installer, simplifying the debugging process.

2. Other commercial tools, like API Spy Studio, also target monitoring at the Windows API level. These tools generally have a wider coverage of the Windows API than the PsfMonitor has, however this extra detail may be either a blessing or a curse (as it could overwhelm you with information about API calls that the PSF fixups are not designed to fix).

3. Process Monitor and ProcessExplorer (and similar).

  • Process Monitor shows the same file and registry captures as the API captures above, except that the capture is performed below the MSIX Runtime, which may have already made changes to the original request from the application.
  • Process Explorer is not a tracing utility, but it can be used to help understand command line arguments, process bitness, loaded modules, and kernel objects.

NoteAlthough the Trace Fixup with PsfMonitor was recommended several years ago, over time Process Monitor has become the first tool recommended for tracing activity.

Depending on your needs, the pre-existing fixups can be added to your package, or you may have to create/code your own fixup.

Here are the most common types of issues, and how they are addressed via PSF are given here:

Issue Type

Issue Description(s)

Fixup Type

Shortcuts

Issues with supporting command line arguments, and specifying a working directory. Also, a package cannot have two shortcuts to the same .exe file (solvable by having separate copies of the launcher calling the same target .exe).

PsfLauncher

Scripts

Applications sometimes require modification either as part of the installation, or prior to use by the end-user. Generally, these modifications are based on the environment (Machine, OS, User, or other).

PsfLauncher

Hidden Files

Although files in certain VFS folders may be present in your package, some like LocalAppData, will not be seen without a fixup.

FileRedirectionFixup or MfrFixup

Writing to Package Files

The application cannot write to or modify files in the package without a fixup that makes a copy for the application.

FileRedirectionFixup or, MfrFixup

Dll Not Found


The application may have difficulty finding dlls that are part of the package.

DynamicLibraryFixup

Writing to Registry

The application cannot write to or modify the machine portion of the registry. It may also have difficulty under the HKCU if the app asks for more permissions than it really needs.

RegLegacyFixups

Blocking Java

Sometimes we must use an older version of Java in the application and must “block” visibility to the newer Java runtime maintained natively on the system.

RegLegacyFixups

Missing Environment Variables

The application cannot see new or changed values to environment variables created or modified in the package.

EnvVarFixup

Using the PsfFtaCom

One of the challenges of moving from registry-based registration of file type associations for Shell Integration Verb commands and Shell Extensions to the declarative AppXManifest-based registration is that the schema restrictions prevent all existing registrations to be Migrated.

These schema restrictions require for example:

  • A given file type may only be defined once, and against a single Application. This prevents a package with two apps adding two different shell verbs for the same file type that point to different applications.
  • A given Shell Extension must be placed in the same application with the File Type, and the COM Server CLSID must be in the same application. This causes problems when the same COM component is shared for multiple Shell Extensions.

PsfFtaCom.exe is a launcher for Shell Integration Verbs, allowing a new AppXManifest Application that can house all FTA and application-level COM definitions to exist under a single Application record, overcoming these issues.

PsfFtaCom is used just like PsfLauncher, but the target executable that should be used is now embedded in the ShellVerb field of the AppXManifest.

The PsfFtaCom.exe is only invoked for the shell verb commands. FTA Shell Extension COM use doesn’t use the exe, but the presence in the AppXManifest acts as a place to consolidate all of the FTA Shell Extensions and COM to avoid uniqueness issues.

Psf Dependency Components

In addition to the launchers, the Package Support Framework contains several dependency components, mentioned here, plus the predefined fixups described in the section that follows. The Binary components come in both 32 and 64 bit varieties.

  • PsfRuntime.dll- a dll needed by both PsfLauncher and the fixups.
  • PsfRunDll.exe - a component used when a 32bit app launches a 64bit child process (or visa versa) and the PSF is in use.
  • StartingScriptWrapper.ps1, StartMenuCmdScriptWrapper.ps1, StartMenuShellLaunchWrapperScript.ps1 - Powershell scripts need to solve certain shortcuts or for a runtime script when the application starts/exits.
  • Additional dlls needed by the TraceFixup/PsfMonitor
  • VCRuntime dlls needed by several of the fixups.

Predefined Fixups

In addition to the launcher, the Package Support Framework contains several predefined fixups described below.

ElectronFixup

Electron Apps is used to help apps originally developed as Electron Apps to operate in the MSIX container. This fixup predated the FIleRedirectionFixup and MfrFixup and is no longer commonly used.

FileRedirectionFixup

The File Redirection is used to solve many file-related issues that could occur. The MfrFixup is a preferred replacement to the FileRedirectionFixup. The FileRedirectionFixup is retained mostly for backward compatibility.

- The FileRedirectionFixup acts as an aid, allowing the containerized application process to have visibility of files that were captured under certain VFS folders.

The MSIX runtime generally provides an overlay allowing to check the equivalent VFS path inside a package when an application requests a file of a certain path. Unfortunately, there are VFS folders in the package where this does not apply. Originally the AppData, LocalAppData, and CommonAppData folders were not mapped on this overlay until Microsoft added CommonAppData mapping.

The FileRedirectionFixup can provide the additional missing mapping through the use of copy-on-access (described below).

- The FileRedirectionFixup allows the application to write to package files.

MSIX protects the package files, preventing any corruption to the files: a feature sometimes referred to as "immutability". But, applications often need to write to files contained in the package, either to store settings or data.

The FileRedirectionFixup can use copy-on-access to create a user private copy of the file and allow that copy to be written to.

- The FileRedirectionFixup implements a copy-on-access for package files and a redirection for access to the copied area. (Advanced Installer handles this automatically)

By default, the files are copied to a package's specific location in the user’s LocalAppData\Packages folder, but this location can be specified using a mapped driver or server share location (specifying the user’s AppData\Roaming folder is not possible due to MSIX limitations).

This allows the application to see areas of the package that are normally hidden, and to make file changes as needed.

Additionally, with the FileRedirectionFixup in use, any future attempt by the application to reference the file, will first detect the copied file as a new mapped layer on top of the packaged one.

MfrFixup

The MfrFixup is a more capable, and recommended, replacement for the FileRedirectionFixup. Unlike the FileRedirecionFixup, this fixup was built with an understanding that some file redirection should go to the package specific redirection area (under LocalAppData\Packages), but other redirection should go to standard shared locations such as the user’s documents folder.

The MfrFixup is also built with a base configuration to solve all file based issues for most applications, with overrides to not do things. This compares to the original FileRedirectionFixup that did nothing by default unless the redirection actions were specifically called out.

It is also written to work in conjunction with the newer Schema extension called InstalledLocationVirtualization, which is a very capable redirection solution for a subset of the package files. A detailed look at InstalledLocationVirtualization is available in an excellent white paper MSIX InstalledLocationVirtualization (tmurgent.com).

DynamicLibraryFixup

Dynamic Library Loading - Used to make sure that the application can find dlls that are in the package.

The MSIX runtime does not implement two of the most important methods for an application installer to specify additional folders to find dll files:

  • Modification to the system PATH environment variable.
  • AppPaths registration.

The DynamicLibraryFixup allows for the registration of package dlls in the Json based configuration, letting the intercept to cause dll loading to always find the dll in the package.

RegLegacyFixups

RegLegacyFixups is intended to be a place for several types of fixups involving the application registry access. As it is relatively new, we expect that it will be updated over time. Currently, it has two types of fixup rules that may be applied as needed.

- RegistryPermissions - Used to modify application registry requests to the permissions allowed under MSIX.

Often an application might open a registry key or item with more permissions than it really needs. Especially for HKCU based items, this could be done in traditional applications, but not under MSIX due to immutability implementations in the MSIX runtime. This intercept rule can target certain types of SAM permission requests made when accessing the registry.

Assuming the application was asking for permissions it did not need, the Registry Permissions solves the problem. If the app later attempts, in a subsequent call, to use those unavailable permissions on the object, the call will fail. But even so, this might still solve your problem as developers tend to specifically check for permissions when executing such requests, so they handle any eventual errors better.

- Fake Delete - Used to trick the application into thinking that a registry item or key has been deleted.

We have found some apps that use the registry as a scratch pad for temporary storage, and then try to delete the entries as part of shutting down. As the MSIX runtime does not permit these deletions, using this tool to "lie" to the app could solve the issue.

- Deletion Markers - The ability to hide a local registry key/value. Microsoft championed this as a solution to “the Java problem”, however the next item was created to simplify the solution.

- JavaBlock - Rather than have to list the specific registry keys to block, this solution requires only the configuration of the Java Version included in the package, and any locally installed Java Runtime that is newer is hidden.

EnvVarFixup

Environment Variables - Used to turn User Session or System scoped environment variables into Application scoped ones.

This affects only processes running inside the container, so it does not help with things such as Path variable changes.

The fixup supports registering the environment variable name and the value inside the JSON configuration, or alternatively, registering the name in the JSON and the value in the package registry. Both methods allow processes inside the container to see the value, and the alternative method supports apps that want to update the value at runtime.

Applying the PSF

Although Microsoft offers the Package Support Framework as an open-source kit on its GitHub page, they do not provide support to make it easy to integrate it into your packaging and debugging workflow -- and you are left with cobbling up your own workflow using independent pieces, or resorting to third party tools.

Third party packaging tools tend to have a more integrated and holistic approach to simplifying and speeding up this process. Be aware that third party products may either use an older version of the PSF than the one available on GitHub, modified versions of prebuilt fixups, or could include additional kinds of fixups that are not available in the GitHub source.

Applying PSF for MSIX Packaging Tool

If you are using the MSIX Packaging Tool, they recently added some direct support for adding their version of the PSF into the package. In the package editor, you can first run an analyzer tool that will make some suggestions. It will then be up to you to add in the PSF components and edit the config.json file.

But the best way to leverage the Package Support Framework within your MSIX package (without having to do everything manually) is to use the free PsfTooling kit, or TMEditX, both built by Tim Mangan.

PsfTooling can analyze, apply, and configure all of the prebuilt fixups described previously, with the exception of the ElectronFixup. It also supports a standard fixup called WaitForDebugger, which is useful for developers that need to debug their code or for debugging the PSF itself.

Additionally the tool can detect and modify the installed application to improve compatibility by aligning the application component registration to methods supported by the MSIX Packaging Tool.

PsfTooling is used during the installation/monitor phase of the recapture.

TMEditX, which contains the same analysis and capabilities as PsfTooling, is run after the initial package creation. While it could be used to fix up an MSIX package created by any vendor tool, it is primarily used to fix up those created by the Microsoft Tool. Because TMEditX doesn’t depend on Microsoft to write the final package, TMEditX provides additional analysis and fixups that would not be possible with PsfTooling.

Applying PSF with Advanced Installer

Advanced Installer Express - the free, featured-limited edition of Advanced Installer - offers support for additional fixups and replacements for predefined fixups: these can be easily configured and integrated into your package by using the “Trace App” functionality.

The following equivalent replacements should be considered:

  • Trace App
  • WorkingDirectory
  • Command-line arguments management
  • Run custom PowerShell scripts

If you encounter any situations for which a fixup is not available, contact the Advanced Installer Support team over email at support@advancedinstaller.com. They will analyze the problem and try to provide a fixup for the community, if possible.

Advanced Installer Express edition has a built-in debugger that traces your app and tells you what known fixups are required. Check out this video demo at Advanced Installer - Package Support Framework.

Applying PSF with other Packaging Vendors

Flexera AdminStudio, RayNet RayPack Studio, and Master Packager are tools that also provide an integrated approach similar to Advanced Installer.

Other vendors have entered this field recently, often integrating other aspects of the automation and/or other portions of application lifecycle. Some of these vendors integrate Microsoft and/or TMurgent technology as part of their solution. Some of the currently known vendors in this space include Rimo3, AppCure, and Juriba.

Config.JSON and Config.XML

The Package Support Framework uses the Config.Json file inside the package in order to configure the Psflauncher and Fixup components.

The choice of using json inside the package appears to have nothing to do with traditional json usages. There are no web servers or rest interfaces in use. It is just a structured file with configuration items and values.

One important rule you must follow in json is that item names (not values) are always in CamelCase, which means it starts with a lowercase letter and uses an uppercase letter only when it would be a new word if spoken out loud (e.g. “workingDirectory”).

The config files make a heavy use of RegEx in the configuration values. RegEx is a pattern matching syntax, that instructs the reader to use a configured value string as a pattern to match against. The RegEx syntax is different from the pattern matching syntax used by the Windows command processor. If you need to further understand the RegEx syntax, you can start with the information available here and look for the Ecml variation of RegEx.

In addition to the Json format, Microsoft defines an xml version of the file that may be used externally by tools responsible for producing the package. Those tools would transform the xml format into Json as part of the packaging process. To many, xml may be a better choice as it is self describing and makes it easier to implement validators against a schema. This is the main reason Microsoft specifically defined it to be used by these tools, even if the PSF doesn’t use this form itself.

The config file format consists of two major sections, Applications and Processes:

Config Applications Section

The Applications section is used to configure the launcher. The launcher is usually configured elsewhere to become the target of an entrypoint into the container. Generally, we consider this the replacement target of a shortcut, but it can also be the target of other modified registrations in the AppXManifest, such as file type associations or URL protocol handlers.

The Applications section consists of an array of application items.

The identification of the application is performed by matching the name of the launcher process against the id field of the application item “id".

When using the Microsoft MSIX Packaging Tool with PsfTooling to add the PsfLauncher as defined on GitHub, the launcher uses an undocumented algorithm against the process name to perform this comparison. Generally speaking, it drops the “.exe”, turns it into uppercase, and changes any numeric character into the alphabetic English language equivalent (in Pascal Case). For example, the process name “PsfLauncher1.exe” would match the id “PSFLAUNCHEROne”.

NoteOther packaging tools may use a different algorithm in their copy of the PSF and the choice of the id name.

When there is a match found, the rest of the fields of the application item will be implemented by the launcher process.

This includes the following items:

  • monitor - An item with subitems to specify a monitor program, like PsfMonitor, to launch first.
  • scripts - An item with subitems to specify a start script to run before launching the target application, and an end script to run when the target application ends.
  • executable - The package relative path to the ultimate target application file. This may be an .exe file, but any other file type that has a local file type association may be used.
  • workingDirectory - A full or package relative folder to use as the working directory for the ultimate target application. If specified as an empty string, it will use the path containing the target application.
  • arguments - Additional command line arguments to be appended to the command line when launching the ultimate target application.

Config Processes Section

The Applications section is used to configure PSF Fixups on a per-process basis. The section consists of an array of process entries, with two subitems:

  • executable - The value is a RegEx pattern string to match against process names.
  • fixups - The value is an array of fixups. Each fixup has additional subitems:

- dll - The value of this item is the name (without path) of the dll fixup to be injected. If not found by that name, it will attempt to add a 32 or 64 (depending on the bitness of the running process) to the base name and try again. Thus “FileRedirectionFixup.dll” can match “FileRedirectionFixup64.dll”.

- Config - The value is a collection of further subitems, dependent on the needs of the particular fixup.

The entries in the processes section of the json file are an ordered set of rules. Processing of these rules go from top to bottom until a match of the regex entry of the process name matches the name of the new process; the contents of the config for that entry is then used. This allows for adding rules to the top of the list to exclude certain executables from using the PSF.

The best way to begin to understand the syntax further might be to look at some example config files of existing packages.

Inspecting Fixups that are in a Package

Do you want to know if a package uses PSF fixups? Here are a couple of options to inspect it.

The first one is simple, extract the MSIX package using 7-Zip or a similar tool. In the package contents, you should notice the PSF-specific resources (psf launcher, dlls, & the config.json file).

NoteOpening up the package in a zip utility will break the package if saved, so remember to always use a copy of the package!

To understand the exact configuration of the fixups, if present, open the config.json file. Often, this file will be at the root folder of the package, but it could also be stored in the folder with the primary executable (eg. under VFS\ProgramFilesx64\VendorName).

Here is an example:

A Json file that causes the FileRedirectionFixup to be injected into the target process and configures the file redirection for all files with the extension “.log” will look like the one below.

{
	"applications": [
    	{
        	"id": "PSFLAUNCHER1",
        	"executable": "SampleApp/Sample.exe",
        	"workingDirectory": "SampleApp/"
    	}
	],
	"processes": [
    	{
        	"executable": "Sample",
        	"fixups": [
            	{
                	"dll": "FileRedirectionFixup.dll",
                	"config": {
                    	"redirectedPaths": {
                        	"packageRelative": [
                            	{
                                	"base": "SampleApp/",
                                	"patterns": [
                                    	".*\\.log"
                                	]
                            	}
                        	]
                    	}
                	}
            	}
        	]
    	}
	]
}

A sample configuration JSON file

The JSON file uses Regular Expression (RegEx) patterns in many of the fields. The flavor used is known as the ECML variety that is also used in JavaScript. So the “.*\\log” in RegEx means a file that starts with anything, but ends with a case sensitive “.log” file extension. Microsoft describes the RegEx language in this documentation.

Another useful tool for inspecting fixups is “MSIX Hero”. This free application informs you about any PSF fixups present in the package. Here is how it looks when inspecting the same MSIX package.

The same sample configuration file - interpreted by MSIX Hero

For the latest updates on the Package Support Framework, join the Microsoft PSF community.

advanced MSI packaging

YOU’RE READING

MSIX Packaging Fundamentals

by Tim Mangan, Bogdan Mitrache & Kevin Kaminski

Download ebook