Managing Non-VFS Files when working with an MSIX-packaged application

Written by Bogdan Mitrache · October 24th, 2021

Why does my MSIX app fail to find files from the package? The short answer is that this is related to how the files inside the VFS folder are, or are not, virtualized.

In this article, you will learn:

- Why is this happening?

- A little more information about VFS folders and virtualization inside an MSIX container.

- How MSIX handles files installed outside of the well-known VFS folders.

- How to avoid the application from failing when the files are not stored in the VFS folder.

If you prefer to listen instead of reading, here is the webinar recording where I cover the same topic:

NoteStarting with version 18.9, Advanced Installer automatically fixes the MSIX packages that fit the below description. The article below just explains the steps Advanced Installer automatically performs behind the scenes.

Why does a working Win32 application fail to run when packaged as an MSIX?

All MSIX packaged applications store their installation files in a read-only path under the following folder: “Program Files\WindowsApps\”.

This means that all the applications will have their resource access virtualized (redirected) by the OS, in order to find their resources under this new read-only location, even if the applications expect (or believe) that they are loading a resource from a folder like System32 or AppData.

Thanks to this OS virtualization, most Win32 applications can run without any code changes when deployed via an MSIX package.

The OS has a predefined list of well-known folders (System32, Program Files (x86), etc) that are dynamically merged at runtime with the corresponding directories found in the MSIX app package (under the VFS folder).

To sum up, the reason the application fails in this scenario is that these files are located inside a package folder that is not part of the list of redirected locations (see full list below). So, they are not loaded at runtime through the OS virtualization process (even if the files are present in the VFS folder).

What is the VFS folder?

Each MSIX package contains a folder named "VFS". Under the VFS folder of your app package, you can find a list of various predefined system folders, for example, AppData, ProgramFilesX64, System32, etc.

OS virtualization enables merging any "READ files" from the folders that are located in the VFS directory at runtime, along with their native counterparts.

For example, an application could contain the following DLL file as part of its app package:

C:\Program Files\WindowsApps\[package_name]\VFS\SystemX86\vc10.dll

However, when the application runs, the file would appear to be installed at a different location, such as

C:\Windows\System32\vc10.dll

But as we talked about above, merging at runtime maintains compatibility with desktop applications that may expect files to be placed in non-package locations, without requiring any code changes in the application.

But why do some applications still fail to run when these folders are redirected?

Well, part of the answer could be the fact that some applications install files in non-virtualized system locations. You will find a list of all the virtualized system folders below:

NoteThis list was provided by Microsoft in the following article.

System Location

Redirected Location (Under [PackageRoot]\VFS)

Valid on architectures

FOLDERID_SystemX86

SystemX86

x86, amd64

FOLDERID_System

SystemX64

amd64

FOLDERID_ProgramFilesX86

ProgramFilesX86

x86, amd6

FOLDERID_ProgramFilesX64

ProgramFilesX64

amd64

FOLDERID_ProgramFilesCommonX86

ProgramFilesCommonX86

x86, amd64

FOLDERID_ProgramFilesCommonX64

ProgramFilesCommonX64

amd64

FOLDERID_Windows

Windows

x86, amd64

FOLDERID_ProgramData

Common AppData

x86, amd64

FOLDERID_System\catroot

AppVSystem32Catroot

x86, amd64

FOLDERID_System\catroot2

AppVSystem32Catroot2

x86, amd64

FOLDERID_System\drivers\etc

AppVSystem32DriversEtc

x86, amd64

FOLDERID_System\driverstore

AppVSystem32Driverstore

x86, amd64

FOLDERID_System\logfiles

AppVSystem32Logfiles

x86, amd64

FOLDERID_System\spool

AppVSystem32Spool

x86, amd64

If an application installs a file under a different folder (for example under the User Profile folder), the OS will not virtualize the folder at runtime and the application will fail to read the resources required.

TipMSIX installs files only under the WindowsApps folder, it can't place files directly in the real User Profile folder.

NoteSome applications also fail because they try to write to certain files found in VFS locations, or in the registry. If you're curious to read more about why this happens, check the details in the Package Support Framework Introduction article.

How to handle files located in folders not present in the VSF list

If your application also installs files under folders not present in the list above or it requires to write permissions to some files from the above folders, you must take two additional steps when building the MSIX.

First step: Make a copy outside of the installation folder

You need to make a copy of those folders in a location outside of the installation folder. We recommend you copy each non-virtualized folder under this per-application folder:

AppData\Local\Packages\[your-application-folder]\AppData

This folder is unique for each app package and it will be cleaned up during the uninstall by the OS, keeping your application from cluttering the system with junk files.

You can copy these files using PowerShell scripts and the Package Support Framework (PSF).

Here is the script sample from my webinar, that you can use if you are using the MSIX Packaging Tool you need to manually fix the package. Please note that you need to update this script for each of your packages, with the right folder/file paths that you need to copy.

$sourceFolder = "C:\Program Files\WindowsApps\CiscoSystemsInc..CiscoCLIAnalyzer_3.6.8.0_x64__1p1dvntvn6cpe\VFS\Profile\.ciscoSA"
New-Item -Path $env:userprofile -Name ".ciscoSA" -ItemType "directory"
Copy-Item -Path $sourceFolder -Recurse -Destination "$env:userprofile\.ciscoSA" -Container

The PSF allows you to trigger a PowerShell script when the app is launched. If the files are not already present in the destination folder, you can use that trigger to copy all the files and folders you need.

To run the PS1 script you need to configure the PSF config.json file as shown below. Make sure you update the script to use your application executable name and script filename.

{
  "applications": [
    {
      "id": "App",
      "executable": "nw.exe",
      "workingDirectory": "",
      "stopOnScriptError": true,
      "startScript": {
        "scriptPath": "CopyFolder.ps1",
        "showWindow": false,
        "waitForScriptToFinish": true,
        "runOnce": true
      }
    }
  ]
}
Automatic file redirection for non-VFS resources

In practice, we have noticed that some applications that fail also install files under one of these locations (including subfolders):

  • User Profile
  • Public
  • Common Application Data
  • Application Data
  • IIS WWW Root
  • Templates
  • Administrative Tools

If your application installs files in one of these folders and you do not have access to its code to change it, the only solution to make it work is the one explained in this article.

Second step: Redirect the application API calls

After you copy the files and folders in the desired folder, you have to make one more configuration: redirect the application API calls for those files.

Since you don’t have access to the source code of your application, you can use the PSF redirection fixup.

After applying the PSF redirection fixup, you will find that when launching your app, it will find the files in the new redirected folder and run as expected.

These steps can be completed with any tool that builds MSIX packages. Some tools provide better integration with the Package Support Framework, making your packaging job easier, while others don’t, thus requiring you to spend more time manually implementing the steps above. With the MSIX Packaging Tool you need to manually configure the PSF. After you add all the PSF binaries as I have shown in the webinar demo, you need to also include the following content in the config.json file, and update it to match your folder structure.

{
  "applications": [
    {
      "id": "App",
      "executable": "nw.exe",
      "workingDirectory": "",
      "stopOnScriptError": true,
      "startScript": {
        "scriptPath": "CopyFolder.ps1",
        "showWindow": false,
        "waitForScriptToFinish": true,
        "runOnce": true
      }
    }
  ],
  "processes": [
        {
            "executable": "PSFSample",
            "fixups": [
                {
                    "dll": "FileRedirectionFixup.dll",
                    "config": {
                        "redirectedPaths": {
                            "packageRelative": [
                                {
                                    "base": ".ciscoSA/",
                                    "patterns": [
                                        ".*\\.*"
                                    ]
                                }
                            ]
                        }
                    }
                }
            ]
        }
    ]
}

NoteAppData files don’t need redirection, this is done automatically done by the OS. All you need to do is to copy the files from the package in the Packages private folder. Any other folder you wish to redirect to a private location under Package folder also requires using the FileRedirectionFixup.

Now you know how to fix your MSIX application when it fails to find files from the package. Have any questions? Leave them below.

Comments: