diff --git a/msix-src/psf/package-support-framework-overview.md b/msix-src/psf/package-support-framework-overview.md index 52e90c97..6dbfd024 100644 --- a/msix-src/psf/package-support-framework-overview.md +++ b/msix-src/psf/package-support-framework-overview.md @@ -1,49 +1,200 @@ --- -description: The Package Support Framework helps you fix issues that prevent your desktop application from running in an MSIX container. -title: Package Support Framework -ms.date: 07/02/2026 -ms.topic: how-to -keywords: windows 10, uwp, msix +description: Learn what the Package Support Framework is, which components and runtime fixes it includes, how it's released, and where to find step-by-step guidance. +title: Package Support Framework overview +ms.date: 08/24/2026 +ms.topic: overview +keywords: windows 10, windows 11, msix, psf, package support framework, runtime fix, fixup, releases ms.custom: RS5 --- -# Package Support Framework Overview +# Package Support Framework overview + +The Package Support Framework (PSF) is an open-source kit that applies fixes to an existing desktop +application when you don't have access to its source code, so that the application can run in an +MSIX container. The PSF helps an application follow the practices of the modern runtime environment. + +The PSF applies its fixes at runtime, in the processes you configure. Applying the PSF changes the +package itself: you add the PSF binaries, point the manifest at the PSF launcher, and repackage and +re-sign the package, as described in [Step 3: Apply a runtime fix](psf-apply-a-runtime-fix.md). At +runtime, the fixes intercept calls made by the processes listed in the configuration file in the +package. They don't change the files that the package installed, and they don't change how other +applications on the device behave. + +The framework is lightweight, and you can use it to address application issues quickly. It's open +source, so you can also consult the community and build on the work of others. + +## When to use the Package Support Framework + +The PSF applies to a full-trust desktop application in an MSIX package. Here are common examples +where the PSF helps: + +- The application can't find DLLs or data files when it's launched, because the process working + directory defaults to the `System32` or `SysWOW64` directory rather than a location in the + package. You can find the working directory the application expects in the shortcut that the + original installer created. For the step-by-step fix, see + [Package Support Framework - Working Directory fixup](psf-current-working-directory.md). +- The application writes to its install directory. This failure typically shows up as **ACCESS + DENIED** results in [Process Monitor](/sysinternals/downloads/procmon) for paths under + `%ProgramFiles%\WindowsApps`. For the step-by-step fix, see + [How to fix Package Support Framework Filesystem Write Permission errors](psf-filesystem-writepermission.md). +- The application must be started with command-line arguments that a shortcut used to supply. For + the step-by-step fix, see + [Package Support Framework - Launching Windows apps with parameters](psf-launch-apps-with-parameters.md). +- The application depends on registry access, environment variables, or DLL load behavior that + differs inside the container. See [Step 2: Find a runtime fix](psf-find-a-runtime-fix.md). + +## What's inside the Package Support Framework + +The PSF contains a launcher executable, a runtime manager DLL, and a set of runtime fixes. + +![Components of the Package Support Framework](images/package-support-framework.png) + +| Component | Role | +|---|---| +| PSFLauncher32.exe, PSFLauncher64.exe | The executable that the package manifest starts in place of your application. It reads *config.json* and starts your application with the PSF runtime injected. | +| PSFRuntime32.dll, PSFRuntime64.dll | The runtime manager. It's injected into the application process, loads the fixups that *config.json* lists for that process, and injects itself into the child processes that the application starts. | +| PSFRunDll32.exe, PSFRunDll64.exe | An in-package replacement for the system *rundll32.exe*. Detours uses *rundll32.exe* to inject into a process of a different architecture, and a process whose executable is outside the package runs without package identity, so it can't load the PSF runtime DLL. Include these binaries when a cross-architecture launch is possible. | +| Fixup DLLs | The runtime fixes, such as *FileRedirectionFixup64.dll*. Each one replaces the implementation of the functions it targets. | +| config.json | The configuration file in the package root. It maps each application ID to an executable, and lists the fixes and settings for each process. | + +Here's the process: + +1. Create a configuration file that specifies the fixes you want to apply to your application. +1. Modify your package manifest to point to the PSF launcher executable. + +When a user starts your application, the PSF launcher is the first executable that runs. It reads +your configuration file and injects the runtime manager DLL into the application process. The runtime +manager loads the runtime fixes that you configured for that process, and a fix takes effect when the +application makes a call that needs it to run inside an MSIX container. + +![The PSF launcher injecting the runtime manager into the application process](images/package-support-framework-2.png) + +The PSF uses [Detours](https://www.microsoft.com/research/project/detours), an open-source framework +developed by Microsoft Research for API redirection and hooking. + +### Runtime fixes + +The PSF includes the following runtime fixes: + +- **FileRedirectionFixup**: redirects file reads and writes from locations the container doesn't + allow the application to write to. +- **RegLegacyFixups**: remediates registry calls, including opening keys with more access than the + container grants, deleting keys and values, and resolving values that live outside the package. +- **EnvVarFixup**: supplies environment variables that a traditional installer would have set on the + system or the user. +- **DynamicLibraryFixup**: redirects DLL load calls to the copy of the DLL in the package. +- **ElectronFixup**: allows a basic Electron application to launch in an AppContainer. Its source is + in the repository, but it isn't part of the PSF solution or the NuGet package, so you build it + yourself. +- **TraceFixup**: a diagnostic tool rather than a fix. It reports the functions the application calls + and whether those calls succeed. Its source is under *tests* in the repository, and its binaries + are in the NuGet package. + +To match a specific failure to a fix, see [Step 2: Find a runtime fix](psf-find-a-runtime-fix.md). +For the configuration schema of each fix, see the +[Package Support Framework repository](https://github.com/microsoft/MSIX-PackageSupportFramework). + +## Package Support Framework releases + +The source code is published in the +[microsoft/MSIX-PackageSupportFramework](https://github.com/microsoft/MSIX-PackageSupportFramework) +repository, and the pre-built binaries are published in the +[Microsoft.PackageSupportFramework](https://www.nuget.org/packages/Microsoft.PackageSupportFramework) +NuGet package. Recent release tags use the form `1.0..`. Recent releases publish +release notes but don't attach binaries to the release, so get the binaries from the NuGet package, +or build them from source. Check the version of the NuGet package you install so that you know which +release you're using. + +The following table lists recent releases. For the complete list and full release notes, see +[Releases](https://github.com/microsoft/MSIX-PackageSupportFramework/releases). + +| Release | Published | Highlights | +|---|---|---| +| [1.0.240212.1](https://github.com/microsoft/MSIX-PackageSupportFramework/releases/tag/1.0.240212.1) | February 2024 | Framework package support. RegLegacyFixups and EnvVarFixup can resolve a value in the context of a package dependency, so an application can retrieve information about its dependencies without global registry or environment variable access. | +| [1.0.231110.2](https://github.com/microsoft/MSIX-PackageSupportFramework/releases/tag/1.0.231110.2) | November 2023 | `DeletionMarker` remediation for RegLegacyFixups, which hides specific registry keys or values in the container. Error messages when *config.json* or *StartingScriptWrapper.ps1* is missing from the package. | +| [1.0.230224.1](https://github.com/microsoft/MSIX-PackageSupportFramework/releases/tag/1.0.230224.1) | February 2023 | `inPackageContext` setting, which runs the processes that an application starts in the same package context, including processes whose executables aren't in the package. Warnings when a start or end script fails, and a fix so that a `runOnce` script retries until it succeeds. | +| [1.0.221230.1](https://github.com/microsoft/MSIX-PackageSupportFramework/releases/tag/1.0.221230.1) | December 2022 | Argument redirection. When the application starts another application and passes a file path as an argument, the PSF rewrites the argument to the redirected per-user copy of that file when one exists. | +| [1.0.220926.1](https://github.com/microsoft/MSIX-PackageSupportFramework/releases/tag/1.0.220926.1) | September 2022 | EnvVarFixup, the `FakeDelete` remediation for RegLegacyFixups, and the `waitForDebugger` setting, which debug builds of the PSF launcher honor and release builds ignore, plus fixes to file redirection, DLL loading, and script handling. | + +Each fixup and the PSF launcher ships with an XML metadata file that declares the version, the +minimum Windows version the fixup requires, a description, and when to use it. For an example, see +[FileRedirectionFixupMetadata.xml](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/fixups/FileRedirectionFixup/FileRedirectionFixupMetadata.xml). +Check the metadata file of each fix you use, because the minimum Windows version differs between +fixes. For example, the ElectronFixup depends on the `CreateFileFromAppW` API and requires +Windows 10, version 1803 (10.0.17134) or later. + +The PSF binaries are built for x86 and x64. On an Arm64 device, an x86 or x64 packaged application +runs under emulation and uses the x86 or x64 PSF binaries that match it. x64 emulation on Arm +requires Windows 11; Windows 10 on Arm supports x86 emulation only, so use the x86 PSF binaries +for emulated applications on Windows 10 on Arm. + +To move a package to a newer PSF release, install the newer NuGet package version, replace the PSF +binaries in your package layout, then repackage, sign, and reinstall the package as described in +[Step 3: Apply a runtime fix](psf-apply-a-runtime-fix.md). Increment the package version when you +repackage, because Windows doesn't install a package over a package that has a higher version. + +## Step-by-step guidance + +Work through the following steps to apply the PSF to your package. For the prerequisites and an +overview of the whole workflow, see +[Get started with the Package Support Framework](package-support-framework.md). + +| Step | What you do | +|---|---| +| [Step 1: Identify compatibility issues](psf-identify-issues.md) | Reproduce the failure and capture it with Process Monitor or the Trace Fixup. | +| [Step 2: Find a runtime fix](psf-find-a-runtime-fix.md) | Match the failure to a fix that ships with the PSF, a community fix, or a new fix you write. | +| [Step 3: Apply a runtime fix](psf-apply-a-runtime-fix.md) | Add the PSF binaries to the package, update the manifest, author *config.json*, and repackage. | +| [Step 4: Debug or extend a runtime fix](psf-debug-a-runtime-fix.md) | Debug the fix, extend it, or build a new one in Visual Studio. | + +Related tasks: + +- [Automated PSF config generation](psf-integration-with-mpt.md) +- [Apply the Package Support Framework in Visual Studio](package-support-framework-vs.md) +- [Run scripts with the Package Support Framework](run-scripts-with-package-support-framework.md) +- [Create a Package Support Framework fixup](create-package-support-framework.md) + +## Limitations + +- The PSF fixes runtime behavior. It doesn't change what the package installs, and it isn't a + substitute for correcting the application itself. +- The PSF requires package identity. The runtime manager reads the package identity and the package + root during initialization, so it works only in a process that runs with package identity, and it + applies to full-trust packaged desktop applications, which declare + `EntryPoint="Windows.FullTrustApplication"` and the `runFullTrust` restricted capability. If the + runtime manager can't initialize, for example because *config.json* is missing from the package + root, it fails to load, and the process usually fails to start. +- The runtime manager is injected into the process that the PSF launcher starts and into the child + processes that process creates. Which fixes apply to each process is decided by matching the + process name against the `processes` array in *config.json*, so a process that doesn't match any + entry runs without fixes. A process that the PSF launcher didn't start, directly or indirectly, + doesn't get the runtime manager at all. To run child processes whose executables aren't in the + package in the package context, use the `inPackageContext` setting, added in release 1.0.230224.1. +- A fix can intercept only the functions that fixup implements. If the application uses an API that + no fixup targets, you need to + [create a fixup](create-package-support-framework.md) that targets it. +- Registry compatibility is handled at runtime by RegLegacyFixups, which modifies registry calls + such as key access requests and deletions. The PSF doesn't author entries into the package's + virtual registry, so use your packaging tool for registry state that must exist at install time. +- The PSF binaries in the NuGet package are x86 and x64 only. -The Package Support Framework (PSF) is an open source kit that helps you apply fixes to your existing desktop application when you don't have access to the source code, so that it can run in an MSIX container. The Package Support Framework helps your application follow the best practices of the modern runtime environment. - -Here are some common examples where you can find the Package Support Framework useful: - -* Your app can't find some DLLs when launched. You may need to set your current working directory. You can learn about the required current working directory in the original shortcut before you converted to MSIX. For a step-by-step fix, see [Windows app working directory error](psf-current-working-directory.md). -* The app writes into the install folder. You will typically see it by "Access Denied" errors in [Process Monitor](/sysinternals/downloads/procmon). For a step-by-step fix, see [How to fix Package Support Framework Filesystem Write Permission errors](psf-filesystem-writepermission.md). -* Your app needs to pass parameters to the executable on launch. You can learn more how PSF can help by going [here](psf-launch-apps-with-parameters.md) and learn more about the available configurations [here](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/master/PsfLauncher). - -This framework is open source, lightweight, and you can use it to address application issues quickly. It also gives you the opportunity to consult with the community around the globe, and to build on top of the investments of others. - -For a step-by-step guide, see [our get started guide](/windows/uwp/porting/package-support-framework). - -## Get started using the Package Support Framework -Here are the tools you will need: - -1. [The Package Support Framework](https://github.com/microsoft/MSIX-PackageSupportFramework/releases) -1. Get [Process Monitor](/sysinternals/downloads/procmon) to identify issues - -You can check our [GitHub](https://github.com/Microsoft/MSIX-PackageSupportFramework/) for all the available fixes. If you find one, you can apply it to your package. Our [step-by-step guide](/windows/uwp/porting/package-support-framework) shows you how to do this. It will also show you how to use the Visual Studio debugger to step through your application and verify that the fix is working and that it has resolved the compatibility issue. - -If you can't find a runtime fix that addresses your issue, you can [create one](create-package-support-framework.md). To do that, you'll identify which function calls fail when your application runs in an MSIX container. Then, you can create replacement functions that you'd like the runtime manager to call instead. This gives you an opportunity to replace the implementation of a function with behavior that conforms to the rules of the modern runtime environment. - -To create the Package Support Framework leverages the [Detours](https://www.microsoft.com/research/project/detours) technology which is an open source framework developed by Microsoft Research (MSR) and helps with API redirection and hooking. - -You can also use the Package Support Framework to run scripts to customize an application for the user environment dynamically. For more information, see [this article](run-scripts-with-package-support-framework.md). - -### Limitations +## Data and telemetry -The Package Support Framework does not support registry overrides. It is designed to resolve run time issues. +The Package Support Framework includes telemetry that collects usage data and sends it to Microsoft +to help improve our products and services. Read Microsoft's +[privacy statement to learn more](https://privacy.microsoft.com/privacystatement). Data is collected +only when both of the following conditions are met: -## Data and telemetry +- The Package Support Framework binaries are used from the + [NuGet package](https://www.nuget.org/packages/Microsoft.PackageSupportFramework) on a Windows + device. +- The user has enabled collection of data on the device. -The Package Support Framework includes telemetry that collects usage data and sends it to Microsoft to help improve our products and services. Read Microsoft's [privacy statement to learn more](https://privacy.microsoft.com/privacystatement). However, data will be collected only when both of the following conditions are met: +The NuGet package contains signed binaries and collects usage data from the device. Telemetry isn't +collected when the binaries are built locally by cloning the repository or downloading the binaries +directly. -* The Package Support Framework binaries are used from the [NuGet package](https://www.nuget.org/packages?q=packagesupportframework) on a Windows 10 computer. -* The user has enabled collection of data on the computer. +## Related content -The NuGet package contains signed binaries and will collect usage data from the computer. Telemetry is not collected when the binaries are built locally by cloning the repo or downloading the binaries directly. +- [Get started with the Package Support Framework](package-support-framework.md) +- [Package Support Framework repository](https://github.com/microsoft/MSIX-PackageSupportFramework) +- [Create an MSIX package](../packaging-tool/create-an-msix-overview.md) diff --git a/msix-src/psf/package-support-framework-vs.md b/msix-src/psf/package-support-framework-vs.md index 8fb0121d..e13417e0 100644 --- a/msix-src/psf/package-support-framework-vs.md +++ b/msix-src/psf/package-support-framework-vs.md @@ -114,7 +114,7 @@ Provide a value for each key. Use this table as a guide. |-------|-----------|-------| | applications | id | Use the value of the `Id` attribute of the `Application` element in the package manifest. | | applications | executable | The package-relative path to the executable that you want to start. In most cases, you can get this value from your package manifest file before you modify it. It's the value of the `Executable` attribute of the `Application` element. | -| applications | workingDirectory | (Optional) A package-relative path to use as the working directory of the application that starts. If you don't set this value, the operating system uses the `System32` directory as the application's working directory. | +| applications | workingDirectory | (Optional) The working directory of the application that starts. A relative path is resolved against the package root. If you omit this value or set it to an empty string, the launcher uses the package root. Without the launcher, a packaged desktop application starts with the `System32` directory as its working directory. | | processes | executable | In most cases, this will be the name of the `executable` configured above with the path and file extension removed. | | fixups | dll | Package-relative path to the fixup DLL to load. | | fixups | config | (Optional) Controls how the fixup DLL behaves. The exact format of this value varies on a fixup-by-fixup basis as each fixup can interpret this "blob" as it wants. | diff --git a/msix-src/psf/package-support-framework.md b/msix-src/psf/package-support-framework.md index cf7a543e..2e4d1f84 100644 --- a/msix-src/psf/package-support-framework.md +++ b/msix-src/psf/package-support-framework.md @@ -1,495 +1,82 @@ --- -description: Learn about and how to use the components in the Package Support Framework to apply fixes to run in an MSIX container. +description: Get started with the Package Support Framework. Work through the four-step workflow that fixes a desktop application that fails in an MSIX container. title: Fix issues that prevent your desktop application from running in an MSIX container -ms.date: 07/02/2026 +ms.date: 08/24/2026 ms.topic: get-started -keywords: windows 10, uwp +keywords: windows 10, windows 11, msix, psf, package support framework, runtime fix, fixup --- -# Get Started with Package Support Framework +# Get started with the Package Support Framework -The [Package Support Framework](package-support-framework-overview.md) is an open source kit that helps you apply fixes to your existing desktop application (without modifying the code) so that it can run in an MSIX container. The Package Support Framework helps your application follow the best practices of the modern runtime environment. +The [Package Support Framework (PSF)](package-support-framework-overview.md) is an open-source kit +that applies fixes to an existing desktop application, without modifying its code, so that the +application can run in an MSIX container. The PSF helps an application follow the practices of the +modern runtime environment. -This article provides an indepth look at each component of Package Support Framework and step by step guide to using it. +The PSF applies to a full-trust desktop application in an MSIX package. It doesn't apply to a UWP +application. -## Understand what is inside a Package Support Framework +This article is the entry point for the four-step workflow. Each step is a separate article, so you +can work through the whole workflow or jump to the step you need. -The Package Support Framework contains an executable, a runtime manager DLL, and a set of runtime fixes. +## Before you begin -![Package Support Framework](images/package-support-framework.png) +Gather the following before you start: -Here is the process: -1. Create a configuration file that specifies the fixes that you want to apply to your application. -1. Modify your package to point to the Package Support Framework (PSF) launcher executable file. +| What you need | Why | +|---|---| +| Your desktop application packaged as an MSIX package, signed and installed on a test device | The PSF fixes runtime behavior in the container, so you need a package that reproduces the failure. See [Create an MSIX package](../packaging-tool/create-an-msix-overview.md). | +| [Process Monitor](/sysinternals/downloads/procmon) | Identifies the failing file system and registry calls. | +| The PSF binaries from the [Microsoft.PackageSupportFramework](https://www.nuget.org/packages/Microsoft.PackageSupportFramework) NuGet package | Provides the launcher, the runtime manager, and the runtime fixes. For what changed in each release, see [Package Support Framework releases](package-support-framework-overview.md#package-support-framework-releases). | +| The [Windows SDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk) | Provides MakeAppx and SignTool for repackaging and signing. | +| Visual Studio, [DebugView](/sysinternals/downloads/debugview), and [Process Explorer](/sysinternals/downloads/process-explorer) | Optional. Needed to debug, extend, or write a runtime fix, and to verify injection. | -When users starts your application, the Package Support Framework launcher is the first executable that runs. It reads your configuration file and injects the runtime fixes and the runtime manager DLL into the application process. The runtime manager applies the fix when it's needed by the application to run inside of an MSIX container. +For the components of the PSF, the fixes it includes, and the current release, see the +[Package Support Framework overview](package-support-framework-overview.md). -![Package Support Framework DLL Injection](images/package-support-framework-2.png) +## The Package Support Framework workflow -## Step 1: Identify packaged application compatibility issues +| Step | What you do | Outcome | +|---|---|---| +| [Step 1: Identify compatibility issues](psf-identify-issues.md) | Reproduce the failure and capture it with Process Monitor or the Trace Fixup. | The failing call, the requested path, and the result, such as **ACCESS DENIED**. | +| [Step 2: Find a runtime fix](psf-find-a-runtime-fix.md) | Match the failure to a fix that ships with the PSF, a community fix, or a new fix you write. | The fixup DLL and the configuration it needs. | +| [Step 3: Apply a runtime fix](psf-apply-a-runtime-fix.md) | Add the PSF binaries to the package, point the manifest at the PSF launcher, author *config.json*, and repackage. | A signed MSIX package that applies the fix, and a verified result. | +| [Step 4: Debug or extend a runtime fix](psf-debug-a-runtime-fix.md) | Set up a Visual Studio solution to debug the fix, extend it, or build a new one. | A runtime fix you can step through and change. | -First, create a package for your application. Then, install it, run it, and observe its behavior. You might receive error messages that can help you identify a compatibility issue. You can also use [Process Monitor](/sysinternals/downloads/procmon) to identify issues. Common issues relate to application assumptions regarding the working directory and program path permissions. +Steps 1 through 3 resolve most issues. Use Step 4 when the applied fix doesn't fully resolve the +failure or when no existing fix matches. -### Using Process Monitor to identify an issue +> [!div class="nextstepaction"] +> [Step 1: Identify compatibility issues](psf-identify-issues.md) -[Process Monitor](/sysinternals/downloads/procmon) is a powerful utility for observing an app's file and registry operations, and their results. This can help you to understand application compatibility issues. After opening Process Monitor, add a filter (Filter > Filter…) to include only events from the application executable. +## Fixes for common failures -![ProcMon App Filter](images/procmon_app_filter.png) +If you already know what the application does wrong, start with the focused walkthrough for that +scenario: -A list of events will appear. For many of these events, the word **SUCCESS** will appear in the **Result** column. +- [Package Support Framework - Working Directory fixup](psf-current-working-directory.md): the + application can't find files that ship in the package, because the process working directory + defaults to the `System32` or `SysWOW64` directory. +- [How to fix Package Support Framework Filesystem Write Permission errors](psf-filesystem-writepermission.md): + the application writes to its install directory under `%ProgramFiles%\WindowsApps` and fails with + an access denied error. +- [Package Support Framework - Launching Windows apps with parameters](psf-launch-apps-with-parameters.md): + the application must be started with command-line arguments that a shortcut used to supply. -![ProcMon Events](images/procmon_events.png) +## Related tasks -Optionally, you can filter events to only show only failures. - -![ProcMon Exclude Success](images/procmon_exclude_success.png) - -If you suspect a filesystem access failure, search for failed events that are under either the System32/SysWOW64 or the package file path. Filters can also help here, too. Start at the bottom of this list and scroll upwards. Failures that appear at the bottom of this list have occurred most recently. Pay most attention to errors that contain strings such as "access denied," and "path/name not found", and ignore things that don't look suspicious. The [PSFSample](https://github.com/Microsoft/MSIX-PackageSupportFramework/blob/master/samples/PSFSample/) has two issues. You can see those issues in the list that appears in the following image. - -![ProcMon Config.txt](images/procmon_config_txt.png) - -In the first issue that appears in this image, the application is failing to read from the "Config.txt" file that is located in the "C:\Windows\SysWOW64" path. It's unlikely that the application is trying to reference that path directly. Most likely, it's trying to read from that file by using a relative path, and by default, "System32/SysWOW64" is the application's working directory. This suggests that the application is expecting its current working directory to be set to somewhere in the package. Looking inside of the appx, we can see that the file exists in the same directory as the executable. - -![App Config.txt](images/psfsampleapp_config_txt.png) - -The second issue appears in the following image. - -![ProcMon Logfile](images/procmon_logfile.png) - -In this issue, the application is failing to write a .log file to its package path. This would suggest that a file redirection fixup might help. - - - -## Step 2: Find a runtime fix - -The PSF contains runtime fixes that you can use right now, such as the file redirection fixup. - -### File Redirection Fixup - -You can use the [File Redirection Fixup](https://github.com/Microsoft/MSIX-PackageSupportFramework/tree/master/fixups/FileRedirectionFixup) to redirect attempts to write or read data in a directory that isn't accessible from an application that runs in an MSIX container. - -For example, if your application writes to a log file that is in the same directory as your applications executable, then you can use the [File Redirection Fixup](https://github.com/Microsoft/MSIX-PackageSupportFramework/tree/master/fixups/FileRedirectionFixup) to create that log file in another location, such as the local app data store. - -> [!TIP] -> If you already know your app fails because it writes to its install directory, see the focused, end-to-end walkthrough in [How to fix Package Support Framework Filesystem Write Permission errors](psf-filesystem-writepermission.md). - -### Runtime fixes from the community - -Make sure to review the community contributions to our [GitHub](https://github.com/Microsoft/MSIX-PackageSupportFramework) page. It's possible that other developers have resolved an issue similar to yours and have shared a runtime fix. - -## Step 3: Apply a runtime fix - -You can apply an existing runtime fix with a few simple tools from the Windows SDK, and by following these steps. - -> [!div class="checklist"] -> * Create a package layout folder -> * Get the Package Support Framework files -> * Add them to your package -> * Modify the package manifest -> * Create a configuration file - -Let's go through each task. - -### Create the package layout folder - -If you have a .msix (or .appx) file already, you can unpack its contents into a layout folder that will serve as the staging area for your package. You can do this from a command prompt using MakeAppx tool, based on your installation path of the SDK, this is where you will find the makeappx.exe tool on your Windows 10 PC: -x86: C:\Program Files (x86)\Windows Kits\10\bin\x86\makeappx.exe -x64: C:\Program Files (x86)\Windows Kits\10\bin\x64\makeappx.exe - -```powershell -makeappx unpack /p PSFSamplePackage_1.0.60.0_AnyCPU_Debug.msix /d PackageContents - -``` - -This will give you something that looks like the following. - -![Package Layout](images/package_contents.png) - -If you don't have a .msix (or .appx) file to start with, you can create the package folder and files from scratch. - -### Get the Package Support Framework files - -You can get the PSF Nuget package by using the standalone Nuget command line tool or via Visual Studio. - -#### Get the package by using the command line tool - -Install the Nuget command line tool from this location: https://www.nuget.org/downloads. Then, from the Nuget command line, run this command: - -```powershell -nuget install Microsoft.PackageSupportFramework -``` - -Alternatively, you can rename the package extension to .zip and unzip it. All the files you need will be under the /bin folder. - -#### Get the package by using Visual Studio - -In Visual Studio, right-click your solution or project node and pick one of the Manage Nuget Packages commands. Search for **Microsoft.PackageSupportFramework** or **PSF** to find the package on Nuget.org. Then, install it. - -### Add the Package Support Framework files to your package - -Add the required 32-bit and 64-bit PSF DLLs and executable files to the package directory. Use the following table as a guide. You'll also want to include any runtime fixes that you need. In our example, we need the file redirection runtime fix. - -| Application executable is x64 | Application executable is x86 | -|-------------------------------|-----------| -| [PSFLauncher64.exe](https://github.com/Microsoft/MSIX-PackageSupportFramework/tree/master/PsfLauncher/#readme) | [PSFLauncher32.exe](https://github.com/Microsoft/MSIX-PackageSupportFramework/tree/master/PsfLauncher/#readme) | -| [PSFRuntime64.dll](https://github.com/Microsoft/MSIX-PackageSupportFramework/tree/master/PsfRuntime/readme.md) | [PSFRuntime32.dll](https://github.com/Microsoft/MSIX-PackageSupportFramework/tree/master/PsfRuntime/readme.md) | -| [PSFRunDll64.exe](https://github.com/Microsoft/MSIX-PackageSupportFramework/blob/master/PsfRunDll/readme.md) | [PSFRunDll32.exe](https://github.com/Microsoft/MSIX-PackageSupportFramework/blob/master/PsfRunDll/readme.md) | - -Your package content should now look something like this. - -![Package Binaries](images/package_binaries.png) - -### Modify the package manifest - -Open your package manifest in a text editor, and then set the `Executable` attribute of the `Application` element to the name of the PSF Launcher executable file. If you know the architecture of your target application, select the appropriate version, PSFLauncher32.exe or PSFLauncher64.exe. If not, PSFLauncher32.exe will work in all cases. Here's an example. - -```xml - - ... - - - ... - - - -``` - -### Create a configuration file - -Create a file name ``config.json``, and save that file to the root folder of your package. Modify the declared app ID of the config.json file to point to the executable that you just replaced. Using the knowledge that you gained from using Process Monitor, you can also set the working directory as well as use the file redirection fixup to redirect reads/writes to .log files under the package-relative "PSFSampleApp" directory. - -```json -{ - "applications": [ - { - "id": "PSFSample", - "executable": "PSFSampleApp/PSFSample.exe", - "workingDirectory": "PSFSampleApp/" - } - ], - "processes": [ - { - "executable": "PSFSample", - "fixups": [ - { - "dll": "FileRedirectionFixup.dll", - "config": { - "redirectedPaths": { - "packageRelative": [ - { - "base": "PSFSampleApp/", - "patterns": [ - ".*\\.log" - ] - } - ] - } - } - } - ] - } - ] -} -``` - -Following is a guide for the config.json schema: - -| Array | key | Value | -|-------|-----------|-------| -| applications | id | Use the value of the `Id` attribute of the `Application` element in the package manifest. | -| applications | executable | The package-relative path to the executable that you want to start. In most cases, you can get this value from your package manifest file before you modify it. It's the value of the `Executable` attribute of the `Application` element. | -| applications | workingDirectory | (Optional) A package-relative path to use as the working directory of the application that starts. If you don't set this value, the operating system uses the `System32` directory as the application's working directory. | -| processes | executable | In most cases, this will be the name of the `executable` configured above with the path and file extension removed. | -| fixups | dll | Package-relative path to the fixup, .msix/.appx to load. | -| fixups | config | (Optional) Controls how the fixup dll behaves. The exact format of this value varies on a fixup-by-fixup basis as each fixup can interpret this "blob" as it wants. | - -The `applications`, `processes`, and `fixups` keys are arrays. That means that you can use the config.json file to specify more than one application, process, and fixup DLL. - -### Package and test the app - -Next, create a package. - -```powershell -makeappx pack /d PackageContents /p PSFSamplePackageFixup.msix -``` - -Then, sign it. - -```powershell -signtool sign /a /v /fd sha256 /f ExportedSigningCertificate.pfx PSFSamplePackageFixup.msix -``` - -For more information, see [how to create a package signing certificate](/windows/desktop/appxpkg/how-to-create-a-package-signing-certificate) -and [how to sign a package using signtool](/windows/desktop/appxpkg/how-to-sign-a-package-using-signtool) - -Using PowerShell, install the package. - ->[!NOTE] -> Remember to uninstall the package first. - -```powershell -powershell Add-AppPackage .\PSFSamplePackageFixup.msix -``` - -Run the application and observe the behavior with runtime fix applied. Repeat the diagnostic and packaging steps as necessary. - -### Check whether the Package Support Framework is running - -You can check whether your runtime fix is running. A way to do this is to open **Task Manager** and click **More details**. Find the app that the package support framework was applied to and expand the app detail to veiw more details. You should be able to view that the Package Support Framework is running. - -### Use the Trace Fixup - -An alternative technique to diagnosing packaged application compatibility issues is to use the Trace Fixup. This DLL is included with the PSF and provides a detailed diagnostic view of the app's behavior, similar to Process Monitor. It is specially designed to reveal application compatibility issues. To use the Trace Fixup, add the DLL to the package, add the following fragment to your config.json, and then package and install your application. - -```json -{ - "dll": "TraceFixup.dll", - "config": { - "traceLevels": { - "filesystem": "allFailures" - } - } -} -``` - -By default, the Trace Fixup filters out failures that might be considered "expected". For example, applications might try to unconditionally delete a file without checking to see if it already exists, ignoring the result. This has the unfortunate consequence that some unexpected failures might get filtered out, so in the above example, we opt to receive all failures from filesystem functions. We do this because we know from before that the attempt to read from the Config.txt file fails with the message "file not found". This is a failure that is frequently observed and not generally assumed to be unexpected. In practice it's likely best to start out filtering only to unexpected failures, and then falling back to all failures if there's an issue that still can't be identified. - -By default, the output from the Trace Fixup gets sent to the attached debugger. For this example, we aren't going to attach a debugger, and will instead use the [DebugView](/sysinternals/downloads/debugview) program from SysInternals to view its output. After running the app, we can see the same failures as before, which would point us towards the same runtime fixes. - -![TraceShim File Not Found](images/traceshim_filenotfound.png) - -![TraceShim Access Denied](images/traceshim_accessdenied.png) - -## Debug, extend, or create a runtime fix - -You can use Visual Studio to debug a runtime fix, extend a runtime fix, or create one from scratch. You'll need to do these things to be successful. - -> [!div class="checklist"] -> * Add a packaging project -> * Add project for the runtime fix -> * Add a project that starts the PSF Launcher executable -> * Configure the packaging project - -When you're done, your solution will look something like this. - -![Completed solution](images/runtime-fix-project-structure.png) - -Let's look at each project in this example. - -| Project | Purpose | -|-------|-----------| -| DesktopApplicationPackage | This project is based on the [Windows Application Packaging project](../desktop/desktop-to-uwp-packaging-dot-net.md) and it outputs the MSIX package. | -| Runtimefix | This is a C++ Dynamic-Linked Library project that contains one or more replacement functions that serve as the runtime fix. | -| PSFLauncher | This is C++ Empty Project. This project is a place to collect the runtime distributable files of the Package Support Framework. It outputs an executable file. That executable is the first thing that runs when you start the solution. | -| WinFormsDesktopApplication | This project contains the source code of a desktop application. | - -To look at a complete sample that contains all of these types of projects, see [PSFSample](https://github.com/Microsoft/MSIX-PackageSupportFramework/blob/master/samples/PSFSample/). - -Let's walk through the steps to create and configure each of these projects in your solution. - -### Create a package solution - -If you don't already have a solution for your desktop application, create a new **Blank Solution** in Visual Studio. - -![Blank solution](images/blank-solution.png) - -You may also want to add any application projects you have. - -### Add a packaging project - -If you don't already have a **Windows Application Packaging Project**, create one and add it to your solution. - -![Package project template](images/package-project-template.png) - -For more information on Windows Application Packaging project, see [Package your application by using Visual Studio](../desktop/desktop-to-uwp-packaging-dot-net.md). - -In **Solution Explorer**, right-click the packaging project, select **Edit**, and then add this to the bottom of the project file: - -```xml - - - - - - <_FilteredNonWapProjProjectOutput Remove="@(_FilteredNonWapProjProjectOutput)" /> - <_FilteredNonWapProjProjectOutput Include="@(FilteredNonWapProjProjectOutput)" /> - - -``` - -### Add project for the runtime fix - -Add a C++ **Dynamic-Link Library (DLL)** project to the solution. - -![Runtime fix library](images/runtime-fix-library.png) - -Right-click the that project, and then choose **Properties**. - -In the property pages, find the **C++ Language Standard** field, and then in the drop-down list next to that field, select the **ISO C++17 Standard (/std:c++17)** option. - -![ISO 17 Option](images/iso-option.png) - -Right-click that project, and then in the context menu, choose the **Manage Nuget Packages** option. Ensure that the **Package source** option is set to **All** or **nuget.org**. - -Click the settings icon next that field. - -Search for the *PSF** Nuget package, and then install it for this project. - -![nuget package](images/psf-package.png) - -If you want to debug or extend an existing runtime fix, add the runtime fix files that you obtained by using the guidance described in the [Find a runtime fix](#find) section of this guide. - -If you intend to create a brand new fix, don't add anything to this project just yet. We'll help you add the right files to this project later in this guide. For now, we'll continue setting up your solution. - -### Add a project that starts the PSF Launcher executable - -Add a C++ **Empty Project** project to the solution. - -![Empty project](images/blank-app.png) - -Add the **PSF** Nuget package to this project by using the same guidance described in the previous section. - -Open the property pages for the project, and in the **General** settings page, set the **Target Name** property to ``PSFLauncher32`` or ``PSFLauncher64`` depending on the architecture of your application. - -![PSF Launcher reference](images/shim-exe-reference.png) - -Add a project reference to the runtime fix project in your solution. - -![runtime fix reference](images/reference-fix.png) - -Right-click the reference, and then in the **Properties** window, apply these values. - -| Property | Value | -|-------|-----------| -| Copy local | True | -| Copy Local Satellite Assemblies | True | -| Reference Assembly Output | True | -| Link Library Dependencies | False | -| Link Library Dependency Inputs | False | - -### Configure the packaging project - -In the packaging project, right-click the **Applications** folder, and then choose **Add Reference**. - -![Add Project Reference](images/add-reference-packaging-project.png) - -Choose the PSF Launcher project and your desktop application project, and then choose the **OK** button. - -![Desktop project](images/package-project-references.png) - ->[!NOTE] -> If you don't have the source code to your application, just choose the PSF Launcher project. We'll show you how to reference your executable when you create a configuration file. - -In the **Applications** node, right-click the PSF Launcher application, and then choose **Set as Entry Point**. - -![Set entry point](images/set-startup-project.png) - -Add a file named ``config.json`` to your packaging project, then, copy and paste the following json text into the file. Set the **Package Action** property to **Content**. - -```json -{ - "applications": [ - { - "id": "", - "executable": "", - "workingDirectory": "" - } - ], - "processes": [ - { - "executable": "", - "fixups": [ - { - "dll": "", - "config": { - } - } - ] - } - ] -} -``` - -Provide a value for each key. Use this table as a guide. - -| Array | key | Value | -|-------|-----------|-------| -| applications | id | Use the value of the `Id` attribute of the `Application` element in the package manifest. | -| applications | executable | The package-relative path to the executable that you want to start. In most cases, you can get this value from your package manifest file before you modify it. It's the value of the `Executable` attribute of the `Application` element. | -| applications | workingDirectory | (Optional) A package-relative path to use as the working directory of the application that starts. If you don't set this value, the operating system uses the `System32` directory as the application's working directory. | -| processes | executable | In most cases, this will be the name of the `executable` configured above with the path and file extension removed. | -| fixups | dll | Package-relative path to the fixup DLL to load. | -| fixups | config | (Optional) Controls how the fixup DLL behaves. The exact format of this value varies on a fixup-by-fixup basis as each fixup can interpret this "blob" as it wants. | - -When you're done, your ``config.json`` file will look something like this. - -```json -{ - "applications": [ - { - "id": "DesktopApplication", - "executable": "DesktopApplication/WinFormsDesktopApplication.exe", - "workingDirectory": "WinFormsDesktopApplication" - } - ], - "processes": [ - { - "executable": ".*App.*", - "fixups": [ { "dll": "RuntimeFix.dll" } ] - } - ] -} - -``` - ->[!NOTE] -> The `applications`, `processes`, and `fixups` keys are arrays. That means that you can use the config.json file to specify more than one application, process, and fixup DLL. - -### Debug a runtime fix - -In Visual Studio, press F5 to start the debugger. The first thing that starts is the PSF Launcher application, which in turn, starts your target desktop application. To debug the target desktop application, you'll have to manually attach to the desktop application process by choosing **Debug->Attach to Process**, and then selecting the application process. To permit the debugging of a .NET application with a native runtime fix DLL, select managed and native code types (mixed mode debugging). - -Once you've set this up, you can set break points next to lines of code in the desktop application code and the runtime fix project. If you don't have the source code to your application, you'll be able to set break points only next to lines of code in your runtime fix project. - -Because F5 debugging runs the application by deploying loose files from the package layout folder path, rather than installing from a .msix/.appx package, the layout folder typically does not have the same security restrictions as an installed package folder. As a result, it may not be possible to reproduce package path access denial errors prior to applying a runtime fix. - -To address this issue, use .msix / .appx package deployment rather than F5 loose file deployment. To create a .msix / .appx package file, use the [MakeAppx](/windows/desktop/appxpkg/make-appx-package--makeappx-exe-) utility from the Windows SDK, as described above. Or, from within Visual Studio, right-click your application project node and select **Store -> Create App Packages**. - -Another issue with Visual Studio is that it does not have built-in support for attaching to any child processes launched by the debugger. This makes it difficult to debug logic in the startup path of the target application, which must be manually attached by Visual Studio after launch. - -To address this issue, use a debugger that supports child process attach. Note that it is generally not possible to attach a just-in-time (JIT) debugger to the target application. This is because most JIT techniques involve launching the debugger in place of the target app, via the ImageFileExecutionOptions registry key. This defeats the detouring mechanism used by PSFLauncher.exe to inject FixupRuntime.dll into the target app. WinDbg, included in the [Debugging Tools for Windows](/windows-hardware/drivers/debugger/index), and obtained from the [Windows SDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk), supports child process attach. It also now supports directly [launching and debugging a UWP app](/windows-hardware/drivers/debugger/debugging-a-uwp-app-using-windbg#span-idlaunchinganddebuggingauwpappspanspan-idlaunchinganddebuggingauwpappspanspan-idlaunchinganddebuggingauwpappspanlaunching-and-debugging-a-uwp-app). - -To debug target application startup as a child process, start ``WinDbg``. - -```powershell -windbg.exe -plmPackage PSFSampleWithFixup_1.0.59.0_x86__7s220nvg1hg3m -plmApp PSFSample -``` - -At the ``WinDbg`` prompt, enable child debugging and set appropriate breakpoints. - -```powershell -.childdbg 1 -g -``` - -(execute until target application starts and breaks into the debugger) - -```powershell -sxe ld fixup.dll -g -``` - -(execute until the fixup DLL is loaded) - -```powershell -bp ... -``` - ->[!NOTE] -> [PLMDebug](/windows-hardware/drivers/debugger/plmdebug) can be also used to attach a debugger to an app upon launch, and is also included in the [Debugging Tools for Windows](/windows-hardware/drivers/debugger/index). However, it is more complex to use than the direct support now provided by WinDbg. +- [Automated PSF config generation](psf-integration-with-mpt.md): let the MSIX Packaging Tool detect + issues and generate the PSF configuration. +- [Apply the Package Support Framework in Visual Studio](package-support-framework-vs.md): add the + PSF to a package you build from a Visual Studio solution. +- [Run scripts with the Package Support Framework](run-scripts-with-package-support-framework.md): + run a PowerShell script before or after the application runs. +- [Create a Package Support Framework fixup](create-package-support-framework.md): write replacement + functions when no existing fix applies. ## Support -Have questions? Ask us on the [Package Support Framework](https://techcommunity.microsoft.com/t5/Package-Support-Framework/bd-p/Package-Support) conversation space on the MSIX tech community site. +Have questions? Ask on the +[Package Support Framework](https://techcommunity.microsoft.com/t5/Package-Support-Framework/bd-p/Package-Support) +conversation space on the MSIX Tech Community site, or open an issue in the +[Package Support Framework repository](https://github.com/microsoft/MSIX-PackageSupportFramework/issues). diff --git a/msix-src/psf/psf-apply-a-runtime-fix.md b/msix-src/psf/psf-apply-a-runtime-fix.md new file mode 100644 index 00000000..b4fc7c1f --- /dev/null +++ b/msix-src/psf/psf-apply-a-runtime-fix.md @@ -0,0 +1,303 @@ +--- +description: Step 3 of the Package Support Framework workflow. Add the PSF binaries to your MSIX package, update the manifest, author config.json, and repackage. +title: "Step 3: Apply a runtime fix to your MSIX package" +ms.date: 08/24/2026 +ms.topic: how-to +keywords: windows 10, windows 11, msix, psf, package support framework, config.json, makeappx, fixup +--- + +# Step 3: Apply a runtime fix to your MSIX package + +This is the third step in the [Package Support Framework (PSF) workflow](package-support-framework.md). +After you choose a runtime fix in [Step 2](psf-find-a-runtime-fix.md), add the PSF to your MSIX +package and configure the fix. + +You can apply an existing runtime fix with tools from the Windows SDK. + +> [!div class="checklist"] +> * Create a package layout folder +> * Get the Package Support Framework files +> * Add the files to your package +> * Modify the package manifest +> * Create a configuration file +> * Package, sign, and test + +> [!TIP] +> If you package with the [MSIX Packaging Tool](../packaging-tool/tool-overview.md), it can detect +> common issues and generate the PSF configuration for you. See +> [Automated PSF config generation](psf-integration-with-mpt.md). If you build your package from a +> Visual Studio solution, see +> [Apply the Package Support Framework in Visual Studio](package-support-framework-vs.md). + +## Create the package layout folder + +If you already have an MSIX package, unpack its contents into a layout folder that serves as the +staging area for the updated package. Use the MakeAppx tool from the Windows SDK, from the folder +that matches the architecture of the device you're working on. Depending on the installation path of +the SDK, you find *makeappx.exe* here: + +- x86: `C:\Program Files (x86)\Windows Kits\10\bin\\x86\makeappx.exe` +- x64: `C:\Program Files (x86)\Windows Kits\10\bin\\x64\makeappx.exe` +- Arm64: `C:\Program Files (x86)\Windows Kits\10\bin\\arm64\makeappx.exe` + +```powershell +makeappx unpack /p PSFSamplePackage_1.0.60.0_AnyCPU_Debug.msix /d PackageContents +``` + +The layout folder looks something like this. + +![Package layout folder contents](images/package_contents.png) + +If you don't have a package to start from, create the package folder and files from scratch. + +## Get the Package Support Framework files + +The PSF binaries ship in the **Microsoft.PackageSupportFramework** NuGet package. Recent releases in +the [Package Support Framework repository](https://github.com/microsoft/MSIX-PackageSupportFramework/releases) +publish release notes but don't attach binaries, so get the binaries from NuGet, or build them from +source. Use either the NuGet command-line tool or Visual Studio. + +### Get the package by using the command-line tool + +Install the NuGet command-line tool from [nuget.org/downloads](https://www.nuget.org/downloads). +Then run this command: + +```powershell +nuget install Microsoft.PackageSupportFramework +``` + +Alternatively, rename the *.nupkg* extension to *.zip* and extract it. The binaries you need are +under the */bin* folder. + +### Get the package by using Visual Studio + +In Visual Studio, right-click the solution or project node and select one of the **Manage NuGet +Packages** commands. Search for **Microsoft.PackageSupportFramework** or **PSF** to find the package +on nuget.org, and then install it. + +To confirm which version you're adding and what changed in it, see +[Package Support Framework releases](package-support-framework-overview.md#package-support-framework-releases). + +## Add the Package Support Framework files to your package + +Copy the PSF binaries into the root of the package layout folder, along with the DLL for each runtime +fix you selected in Step 2. In the following example, the package needs the file redirection fix. + +| Application executable is x64 | Application executable is x86 | +|-------------------------------|-----------| +| [PSFLauncher64.exe](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfLauncher/Readme.md) | [PSFLauncher32.exe](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfLauncher/Readme.md) | +| [PSFRuntime64.dll](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfRuntime/readme.md) | [PSFRuntime32.dll](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfRuntime/readme.md) | +| [PSFRunDll64.exe](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfRunDll/readme.md) | [PSFRunDll32.exe](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfRunDll/readme.md) | + +*PSFRuntime32.dll* and *PSFRuntime64.dll* must keep those names and must be in the package root. +*PSFRunDll32.exe* and *PSFRunDll64.exe* are required only when a cross-architecture launch is +possible, as described in [Modify the package manifest](#modify-the-package-manifest). For the +placement and naming rules that the PSF expects, see +[Package layout](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/layout.md). + +Also copy the fixup DLL for each architecture that the fix is injected into. The fixups that ship +with the PSF are built for both architectures and named with a `32` or `64` suffix, for example +*FileRedirectionFixup32.dll* and *FileRedirectionFixup64.dll*. When the PSF runtime loads a fixup, it +first tries the file name exactly as it appears in *config.json*. If that file isn't found, it +appends the bitness of the current process to the name and tries again. A single entry of +`"dll": "FileRedirectionFixup.dll"` therefore loads *FileRedirectionFixup32.dll* in a 32-bit process +and *FileRedirectionFixup64.dll* in a 64-bit process, so one configuration entry can cover a package +that contains both. For more information, see +[Fixup loading](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfRuntime/readme.md#fixup-loading). + +Your package content should now look something like this. + +![Package layout folder with the PSF binaries added](images/package_binaries.png) + +## Modify the package manifest + +Open the package manifest, *AppxManifest.xml*, in a text editor, and set the `Executable` attribute +of the `Application` element to the PSF launcher executable. Leave the `Id` attribute unchanged: the +launcher uses it to find the matching entry in *config.json*, and changing it breaks existing +shortcuts, execution aliases, and other activation points that reference the application. + +```xml + + ... + + + ... + + + +``` + +Match the launcher to the architecture of your application executable: *PSFLauncher64.exe* for a +64-bit executable, *PSFLauncher32.exe* for a 32-bit executable. A cross-architecture launch, such as +*PSFLauncher32.exe* starting a 64-bit application, is supported, but it does extra work on every +launch and it has an extra requirement. Detours does the injection through a helper process that +matches the architecture of the target process, and the PSF provides *PSFRunDll32.exe* and +*PSFRunDll64.exe* as in-package replacements for the system *rundll32.exe*, which would break away +from the package, run without package identity, and fail to load the PSF runtime DLL. If a +cross-architecture launch is possible in your package, include both the 32-bit and the 64-bit +*PSFRuntime* and *PSFRunDll* binaries. + +Keep `EntryPoint="Windows.FullTrustApplication"`. The PSF applies to full-trust packaged desktop +applications, not to UWP applications. + +> [!NOTE] +> The PSF binaries are built for x86 and x64 only. On an Arm64 device, an x86 or x64 packaged +> application runs under emulation and uses the x86 or x64 PSF binaries that match it. + +> [!NOTE] +> Repeat this change for every `Application` element that needs a runtime fix. A package can declare +> more than one application, and each one has its own entry in *config.json*. + +## Create a configuration file + +Create a file named *config.json*, and save it to the root folder of your package. Set the +application `id` to the value of the `Id` attribute in the package manifest, and set `executable` to +the application executable you replaced with the launcher. + +Using what you learned from Process Monitor in [Step 1](psf-identify-issues.md), you can also set +the working directory and use the file redirection fix to redirect reads and writes of *.log* files +under the package-relative *PSFSampleApp* directory. + +```json +{ + "applications": [ + { + "id": "PSFSample", + "executable": "PSFSampleApp/PSFSample.exe", + "workingDirectory": "PSFSampleApp/" + } + ], + "processes": [ + { + "executable": "PSFSample", + "fixups": [ + { + "dll": "FileRedirectionFixup.dll", + "config": { + "redirectedPaths": { + "packageRelative": [ + { + "base": "PSFSampleApp/", + "patterns": [ + ".*\\.log" + ] + } + ] + } + } + } + ] + } + ] +} +``` + +The following table describes the *config.json* schema. + +| Array | Key | Value | +|-------|-----------|-------| +| applications | id | Use the value of the `Id` attribute of the `Application` element in the package manifest. | +| applications | executable | The package-relative path to the executable that you want to start. In most cases, you can get this value from your package manifest file before you modify it. It's the value of the `Executable` attribute of the `Application` element. | +| applications | workingDirectory | (Optional) The working directory of the application that starts. A relative path is resolved against the package root, and a full path, such as one that begins with a drive letter, is used as it is. If you omit this value or set it to an empty string, the launcher uses the package root. Without the launcher, a packaged desktop application starts with the `System32` directory as its working directory, which is the cause of several common failures. | +| processes | executable | In most cases, this is the name of the `executable` configured above, with the path and file extension removed. The value is an ECMAScript regular expression that's matched against the name of each process the runtime is injected into, so one entry can cover more than one process. | +| fixups | dll | The name of the fixup DLL to load, relative to the package root. If the file isn't found, the PSF runtime appends the bitness of the current process to the name and tries again, so `FileRedirectionFixup.dll` loads *FileRedirectionFixup64.dll* in a 64-bit process. | +| fixups | config | (Optional) Controls how the fixup DLL behaves. The exact format of this value varies on a fixup-by-fixup basis, because each fixup interprets this "blob" as it wants. | + +The `applications`, `processes`, and `fixups` keys are arrays. You can use *config.json* to specify +more than one application, process, and fixup DLL. For the full set of launcher settings, including +arguments, scripts, and monitor processes, see the +[PSF launcher readme](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfLauncher/Readme.md). + +> [!IMPORTANT] +> *config.json* must be named *config.json* and must be in the package root. If the PSF runtime can't +> read its configuration, it fails to load, and the application usually fails to start. Recent PSF +> releases display an error when *config.json* is missing, and when a start or end script is +> configured but *StartingScriptWrapper.ps1* isn't in the package. For more information about +> scripts, see +> [Run scripts with the Package Support Framework](run-scripts-with-package-support-framework.md). + +## Package and test the application + +Create the package. + +```powershell +makeappx pack /d PackageContents /p PSFSamplePackageFixup.msix +``` + +> [!NOTE] +> If the application ships in an MSIX bundle, unpack the bundle with `makeappx unbundle`, update each +> package it contains, and then rebuild the bundle with `makeappx bundle`. + +Increment the `Version` attribute of the `Identity` element in the manifest before you pack, and +leave the `Name` and `Publisher` attributes unchanged so that the result updates the installed +package instead of installing next to it. + +Repacking invalidates the signature of the original package, so sign the new package again. + +```powershell +signtool sign /a /v /fd sha256 /f ExportedSigningCertificate.pfx PSFSamplePackageFixup.msix +``` + +The certificate you sign with must be trusted on the test device, and the `Publisher` value in the +manifest must match the certificate subject exactly. For more information, see +[Create a certificate for package signing](../package/create-certificate-package-signing.md) and +[Sign an app package using SignTool](../package/sign-app-package-using-signtool.md). + +Install the package with PowerShell. + +> [!NOTE] +> Windows doesn't downgrade an installed package: installation fails if the version of the package +> you're installing is lower than the version that's already installed. If you rebuild the package +> without incrementing the version, uninstall the installed package first with +> [Remove-AppxPackage](/powershell/module/appx/remove-appxpackage). + +```powershell +Add-AppxPackage .\PSFSamplePackageFixup.msix +``` + +[Add-AppxPackage](/powershell/module/appx/add-appxpackage) installs the package for the user who runs +the command. To test the fix under a different account, install the package for that user as well, or +provision it for the device so that it installs for each user who signs in. + +Run the application and observe its behavior with the runtime fix applied. Repeat the diagnostic and +packaging steps as needed. + +## Check whether the Package Support Framework is running + +To confirm that the launcher ran, open **Task Manager**, select **More details**, and find both the +PSF launcher process, *PSFLauncher32.exe* or *PSFLauncher64.exe*, and your application process on the +**Details** tab. The launcher starts your application and stays running as its parent process. To see +that parent and child relationship, use the process tree in +[Process Explorer](/sysinternals/downloads/process-explorer). + +To confirm that the runtime manager was injected into the application process, select the application +process in Process Explorer, open the DLL view, and look for *PSFRuntime32.dll* or *PSFRuntime64.dll* +along with the fixup DLLs you configured. The PSF runtime is also injected into the child processes +that your application starts, so check those processes the same way when a failure happens in one of +them. + +If the fix doesn't take effect, check the following: + +- The `id` in *config.json* matches the `Id` attribute in the package manifest. +- The `executable` values point to files that exist in the package, relative to the package root. +- The `executable` value in the `processes` array matches the name of the process that fails, without + a path or a file name extension. +- The package contains the fixup DLL for the architecture of the process it's injected into. +- The regular expressions in the configuration match the paths you captured in Step 1. Patterns are + regular expressions, not wildcards, and JSON requires the backslash to be escaped. For example, + `".*\\.log"` matches log files, while `"*.log"` doesn't. + +## Next step + +> [!div class="nextstepaction"] +> [Step 4: Debug or extend a runtime fix](psf-debug-a-runtime-fix.md) + +## Related content + +- [Get started with the Package Support Framework](package-support-framework.md) +- [Package Support Framework overview](package-support-framework-overview.md) +- [Package Support Framework - Working Directory fixup](psf-current-working-directory.md) +- [How to fix Package Support Framework Filesystem Write Permission errors](psf-filesystem-writepermission.md) +- [Run scripts with the Package Support Framework](run-scripts-with-package-support-framework.md) diff --git a/msix-src/psf/psf-debug-a-runtime-fix.md b/msix-src/psf/psf-debug-a-runtime-fix.md new file mode 100644 index 00000000..f245bf4e --- /dev/null +++ b/msix-src/psf/psf-debug-a-runtime-fix.md @@ -0,0 +1,296 @@ +--- +description: Step 4 of the Package Support Framework workflow. Set up a Visual Studio solution to debug an existing runtime fix, extend it, or build a new one. +title: "Step 4: Debug or extend a runtime fix" +ms.date: 08/24/2026 +ms.topic: how-to +keywords: windows 10, windows 11, msix, psf, package support framework, debug, visual studio, windbg +--- + +# Step 4: Debug or extend a runtime fix + +This is the final step in the [Package Support Framework (PSF) workflow](package-support-framework.md). +Use it when the fix you applied in [Step 3](psf-apply-a-runtime-fix.md) doesn't fully resolve the +issue, when you want to extend an existing fix, or when you're building a new fix. + +You need the following projects in a single Visual Studio solution. + +> [!div class="checklist"] +> * A packaging project +> * A project for the runtime fix +> * A project that produces the PSF launcher executable +> * The application project, if you have the source code + +When you're done, your solution looks something like this. + +![Visual Studio solution with the packaging, runtime fix, launcher, and application projects](images/runtime-fix-project-structure.png) + +Each project in this example serves a distinct purpose. + +| Project | Purpose | +|-------|-----------| +| DesktopApplicationPackage | Based on the [Windows Application Packaging project](../desktop/desktop-to-uwp-packaging-dot-net.md). It produces the MSIX package. | +| Runtimefix | A C++ dynamic-link library project that contains one or more replacement functions that serve as the runtime fix. | +| PSFLauncher | A C++ empty project. It collects the runtime distributable files of the Package Support Framework and produces the launcher executable, which is the first process that starts when you run the solution. | +| WinFormsDesktopApplication | Contains the source code of the desktop application. | + +For a complete sample that contains all of these project types, see +[PSFSample](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/samples/PSFSample). + +## Create a package solution + +If you don't already have a solution for your desktop application, create a **Blank Solution** in +Visual Studio. + +![New blank solution dialog](images/blank-solution.png) + +Add any application projects you have. + +## Add a packaging project + +If you don't already have a **Windows Application Packaging Project**, create one and add it to your +solution. + +![Windows Application Packaging Project template](images/package-project-template.png) + +For more information, see +[Package your application by using Visual Studio](../desktop/desktop-to-uwp-packaging-dot-net.md). + +In **Solution Explorer**, right-click the packaging project, select **Edit**, and then add this to +the bottom of the project file: + +```xml + + + + + + <_FilteredNonWapProjProjectOutput Remove="@(_FilteredNonWapProjProjectOutput)" /> + <_FilteredNonWapProjProjectOutput Include="@(FilteredNonWapProjProjectOutput)" /> + + +``` + +## Add a project for the runtime fix + +Add a C++ **Dynamic-Link Library (DLL)** project to the solution. + +![C++ Dynamic-Link Library project template](images/runtime-fix-library.png) + +Right-click the project, and then select **Properties**. + +In the property pages, find the **C++ Language Standard** field, and select the **ISO C++17 Standard +(/std:c++17)** option. + +![C++ Language Standard set to ISO C++17](images/iso-option.png) + +Right-click the project, and then select **Manage NuGet Packages**. Make sure that the **Package +source** option is set to **All** or **nuget.org**. + +Search for the **PSF** NuGet package, and then install it for this project. + +![PSF NuGet package](images/psf-package.png) + +If you're debugging or extending an existing runtime fix, add the runtime fix files that you +identified in [Step 2: Find a runtime fix](psf-find-a-runtime-fix.md). + +If you're creating a new fix, don't add anything to this project yet. For the code you add here, see +[Create a Package Support Framework fixup](create-package-support-framework.md). + +## Add a project that starts the PSF launcher executable + +Add a C++ **Empty Project** to the solution. + +![C++ Empty Project template](images/blank-app.png) + +Add the **PSF** NuGet package to this project the same way you did in the previous section. + +Open the property pages for the project, and on the **General** settings page, set the **Target +Name** property to `PSFLauncher32` or `PSFLauncher64`, depending on the architecture of your +application. + +![Target Name set to the PSF launcher name](images/shim-exe-reference.png) + +Add a project reference to the runtime fix project in your solution. + +![Project reference to the runtime fix project](images/reference-fix.png) + +Right-click the reference, and then in the **Properties** window, apply these values. + +| Property | Value | +|-------|-----------| +| Copy local | True | +| Copy Local Satellite Assemblies | True | +| Reference Assembly Output | True | +| Link Library Dependencies | False | +| Link Library Dependency Inputs | False | + +## Configure the packaging project + +In the packaging project, right-click the **Applications** folder, and then select **Add Reference**. + +![Add Reference command in the packaging project](images/add-reference-packaging-project.png) + +Select the PSF launcher project and your desktop application project, and then select **OK**. + +![Project references added to the packaging project](images/package-project-references.png) + +> [!NOTE] +> If you don't have the source code to your application, select only the PSF launcher project. You +> reference your executable in the configuration file instead. + +In the **Applications** node, right-click the PSF launcher application, and then select **Set as +Entry Point**. + +![Set as Entry Point command](images/set-startup-project.png) + +Add a file named *config.json* to your packaging project, copy the following JSON into it, and set +the **Package Action** property to **Content**. + +```json +{ + "applications": [ + { + "id": "", + "executable": "", + "workingDirectory": "" + } + ], + "processes": [ + { + "executable": "", + "fixups": [ + { + "dll": "", + "config": { + } + } + ] + } + ] +} +``` + +Provide a value for each key. For the meaning of each key, see the +[*config.json* schema](psf-apply-a-runtime-fix.md#create-a-configuration-file) in Step 3. + +When you're done, your *config.json* file looks something like this. + +```json +{ + "applications": [ + { + "id": "DesktopApplication", + "executable": "DesktopApplication/WinFormsDesktopApplication.exe", + "workingDirectory": "WinFormsDesktopApplication" + } + ], + "processes": [ + { + "executable": ".*App.*", + "fixups": [ { "dll": "RuntimeFix.dll" } ] + } + ] +} +``` + +> [!NOTE] +> The `applications`, `processes`, and `fixups` keys are arrays, so a single *config.json* file can +> specify more than one application, process, and fixup DLL. + +## Debug a runtime fix + +In Visual Studio, press F5 to start the debugger. The PSF launcher starts first and, in turn, starts +your target desktop application. To debug the target application, attach to its process manually by +selecting **Debug** > **Attach to Process** and then selecting the application process. To debug a +.NET application together with a native runtime fix DLL, select both managed and native code types +(mixed-mode debugging). + +After you attach, you can set breakpoints in the desktop application code and in the runtime fix +project. If you don't have the source code for your application, you can set breakpoints only in +your runtime fix project. + +### Reproduce package path failures + +F5 debugging runs the application by deploying loose files from the package layout folder rather +than installing from an MSIX package, so the layout folder usually doesn't carry the same security +restrictions as an installed package folder. As a result, you might not be able to reproduce package +path access denied errors before a runtime fix is applied. + +To work around this, deploy an MSIX package instead of using F5 loose file deployment. Create the +package with the [MakeAppx](/windows/win32/appxpkg/make-appx-package--makeappx-exe-) tool from the +Windows SDK, as described in [Step 3](psf-apply-a-runtime-fix.md), or right-click your application +project node in Visual Studio and select **Publish** > **Create App Packages**. + +### Debug the application startup path + +Visual Studio has no built-in support for attaching to child processes launched by the debugger, +which makes it difficult to debug logic in the startup path of the target application. + +To debug startup, use a debugger that supports attaching to child processes. It generally isn't +possible to attach a just-in-time (JIT) debugger to the target application, because most JIT +techniques launch the debugger in place of the target application through the +`ImageFileExecutionOptions` registry key. That defeats the detouring mechanism that the PSF launcher +uses to inject the runtime manager into the target application. + +WinDbg, included in the [Debugging Tools for Windows](/windows-hardware/drivers/debugger/index) and +available in the [Windows SDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk), +supports attaching to child processes. It also supports +[launching and debugging a packaged application](/windows-hardware/drivers/debugger/debugging-a-uwp-app-using-windbg) +directly. + +To debug target application startup as a child process, start `WinDbg`. The `-plmPackage` value is +the package full name of the installed package, and the `-plmApp` value is the `Id` attribute of the +`Application` element in the package manifest. To get the package full name, run +`Get-AppxPackage | Select-Object PackageFullName`. + +```powershell +windbg.exe -plmPackage PSFSampleWithFixup_1.0.59.0_x86__7s220nvg1hg3m -plmApp PSFSample +``` + +At the `WinDbg` prompt, enable child debugging and set breakpoints. Child debugging is required +because the PSF launcher, not the debugger, starts the application process. + +```console +.childdbg 1 +g +``` + +Execution continues until the target application starts and breaks into the debugger. + +```console +sxe ld FileRedirectionFixup64.dll +g +``` + +Execution continues until the fixup DLL is loaded. Use the file name of the fixup DLL that's in your +package, including the `32` or `64` suffix. + +Now that the fixup DLL is loaded, set a breakpoint on one of its replacement functions and continue. +The debugger needs the symbol file (*.pdb*) that you built with the fixup to resolve the function +name. + +```console +bp FileRedirectionFixup64! +g +``` + +> [!NOTE] +> [PLMDebug](/windows-hardware/drivers/debugger/plmdebug) can also attach a debugger to an +> application on launch, and is also included in the +> [Debugging Tools for Windows](/windows-hardware/drivers/debugger/index). It's more complex to use +> than the child process support that WinDbg provides. + +## Support + +Have questions? Ask on the +[Package Support Framework](https://techcommunity.microsoft.com/t5/Package-Support-Framework/bd-p/Package-Support) +conversation space on the MSIX Tech Community site, or open an issue in the +[Package Support Framework repository](https://github.com/microsoft/MSIX-PackageSupportFramework/issues). + +## Related content + +- [Get started with the Package Support Framework](package-support-framework.md) +- [Step 3: Apply a runtime fix to your MSIX package](psf-apply-a-runtime-fix.md) +- [Create a Package Support Framework fixup](create-package-support-framework.md) +- [Apply the Package Support Framework in Visual Studio](package-support-framework-vs.md) +- [Package Support Framework overview](package-support-framework-overview.md) diff --git a/msix-src/psf/psf-find-a-runtime-fix.md b/msix-src/psf/psf-find-a-runtime-fix.md new file mode 100644 index 00000000..b7cedec6 --- /dev/null +++ b/msix-src/psf/psf-find-a-runtime-fix.md @@ -0,0 +1,105 @@ +--- +description: Step 2 of the Package Support Framework workflow. Match the failure you captured to a runtime fix that ships with the PSF, a community fix, or a new fix. +title: "Step 2: Find a runtime fix" +ms.date: 08/24/2026 +ms.topic: how-to +keywords: windows 10, windows 11, msix, psf, package support framework, fixup, runtime fix, file redirection +--- + +# Step 2: Find a runtime fix + +This is the second step in the [Package Support Framework (PSF) workflow](package-support-framework.md). +After you identify the failing call in [Step 1](psf-identify-issues.md), match that failure to a +runtime fix. + +A runtime fix is a DLL that the PSF runtime manager loads into the application process. The DLL +intercepts the function calls that fail in an MSIX container and replaces them with an +implementation that succeeds. You configure each fix in the package's *config.json* file, so a fix +applies to the processes it's configured for, not to the whole package or to the machine. The PSF +runtime is also injected into the child processes that your application starts, and the `processes` +array in *config.json* decides which fixes apply to each of those processes. + +## Runtime fixes included with the Package Support Framework + +| Runtime fix | Use it when | +|---|---| +| [FileRedirectionFixup](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/FileRedirectionFixup) | The application reads or writes files in a location that isn't writable from the container, such as its own install directory under `%ProgramFiles%\WindowsApps`. The fix redirects the matching paths to a writable per-user location. | +| [RegLegacyFixups](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/RegLegacyFixups) | The application opens registry keys with access rights it doesn't need and fails with an access error, deletes registry keys or values, or expects a registry value that lives outside the package. Remediation types include `ModifyKeyAccess`, `FakeDelete`, `DeletionMarker`, and `Redirect`. | +| [EnvVarFixup](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/EnvVarFixup) | The application depends on an environment variable that a traditional installer would have set on the system or the user. The fix returns the value from *config.json* or from the package registry hive. | +| [DynamicLibraryFixup](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/DynamicLibraryFixup) | The application fails to load a DLL that ships in the package because it loads the DLL by name from a location the loader doesn't search. | +| [ElectronFixup](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/ElectronFixup) | A basic application built on the Electron framework doesn't launch in an AppContainer, that is, in a package that doesn't run with full trust. This fix requires the `CreateFileFromAppW` API, so the device must run Windows 10, version 1803 (10.0.17134) or later. The fix isn't part of the Package Support Framework solution or the NuGet package, so build it from the repository. | +| [TraceFixup](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/tests/fixups/TraceFixup) | You need diagnostics rather than a fix. It reports which functions the application calls and whether the calls succeed. The binaries are in the NuGet package as *TraceFixup32.dll* and *TraceFixup64.dll*. See [Step 1](psf-identify-issues.md#use-the-trace-fixup). | + +Some behavior is configured on the PSF launcher rather than on a fixup DLL. Setting the working +directory, passing arguments to the application executable, running a script before or after the +application, and running child processes that aren't part of the package in the package context, with +the `inPackageContext` setting, are all +[PSF launcher](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfLauncher/Readme.md) +settings in *config.json*. + +The list of fixes changes between releases. For the current set and the configuration schema of +each fix, see the [Package Support Framework repository](https://github.com/microsoft/MSIX-PackageSupportFramework) +and the release notes in [Package Support Framework releases](package-support-framework-overview.md#package-support-framework-releases). + +## Match the failure to a fix + +Use the evidence you recorded in Step 1. + +| What you saw in Step 1 | Fix to start with | Focused walkthrough | +|---|---|---| +| **NAME NOT FOUND** or **PATH NOT FOUND** for a file that ships in the package, requested from `System32` or `SysWOW64` | Set `workingDirectory` on the application in *config.json* | [Package Support Framework - Working Directory fixup](psf-current-working-directory.md) | +| **ACCESS DENIED** with **Desired Access: Generic Write** under `%ProgramFiles%\WindowsApps` | FileRedirectionFixup | [How to fix Package Support Framework Filesystem Write Permission errors](psf-filesystem-writepermission.md) | +| The application requires command-line arguments that a shortcut used to supply | `arguments` on the application in *config.json* | [Package Support Framework - Launching Windows apps with parameters](psf-launch-apps-with-parameters.md) | +| The application starts a helper process whose executable isn't in the package, and that process fails or writes to the wrong location | `inPackageContext` on the application in *config.json* | [PSF launcher readme](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfLauncher/Readme.md) | +| **ACCESS DENIED** opening a registry key, or a failed registry delete | RegLegacyFixups | [RegLegacyFixups readme](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/RegLegacyFixups) | +| The application reads an environment variable that no longer exists | EnvVarFixup | [EnvVarFixup readme](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/EnvVarFixup) | +| A DLL that ships in the package fails to load | DynamicLibraryFixup | [DynamicLibraryFixup source](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/DynamicLibraryFixup) | + +These pairings are a starting point, not an exhaustive mapping. An application can hit more than one +issue, as the [PSFSample](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/samples/PSFSample) +application does, and you can list more than one fix in the `fixups` array of a single process. + +### Example: File Redirection Fixup + +The [FileRedirectionFixup](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/fixups/FileRedirectionFixup) +redirects attempts to read or write data in a directory that the application can't write to from an +MSIX container. For example, if the application writes a log file to the directory that holds its +own executable, the fix creates and uses that log file in a writable location instead. Configure +which paths are redirected with the `redirectedPaths` element, which accepts three kinds of base +path: `packageRelative` for paths under the package installation folder, `packageDriveRelative` for +paths on the drive where the package is installed, and `knownFolders` for locations that +[SHGetKnownFolderPath](/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath) resolves. + +The redirected copy is written per user. Unless you set `redirectTargetBase`, the fix redirects to a +VFS folder under the user's local app data folder, and the first access copies the file there. Each +user therefore works with a separate copy of the data, and that copy lives outside the package. + +> [!TIP] +> If your application fails because it writes to its install directory, follow the end-to-end +> walkthrough in [How to fix Package Support Framework Filesystem Write Permission errors](psf-filesystem-writepermission.md). +> It covers capture, configuration, repackaging, and validation for that scenario. + +## Runtime fixes from the community + +Review the community contributions in the +[Package Support Framework repository](https://github.com/microsoft/MSIX-PackageSupportFramework). +Another developer might have resolved an issue similar to yours and shared a runtime fix. + +## When no existing fix applies + +If none of the available fixes address your issue, you can write one. A new fix declares replacement +functions for the calls that fail and, optionally, reads its own configuration data from +*config.json*. For details, see +[Create a Package Support Framework fixup](create-package-support-framework.md), then debug it by +following [Step 4](psf-debug-a-runtime-fix.md). + +## Next step + +> [!div class="nextstepaction"] +> [Step 3: Apply a runtime fix to your MSIX package](psf-apply-a-runtime-fix.md) + +## Related content + +- [Package Support Framework overview](package-support-framework-overview.md) +- [Create a Package Support Framework fixup](create-package-support-framework.md) +- [Run scripts with the Package Support Framework](run-scripts-with-package-support-framework.md) diff --git a/msix-src/psf/psf-identify-issues.md b/msix-src/psf/psf-identify-issues.md new file mode 100644 index 00000000..774ae172 --- /dev/null +++ b/msix-src/psf/psf-identify-issues.md @@ -0,0 +1,178 @@ +--- +description: Step 1 of the Package Support Framework workflow. Use Process Monitor and the Trace Fixup to identify why a desktop application fails in an MSIX container. +title: "Step 1: Identify compatibility issues in an MSIX container" +ms.date: 08/24/2026 +ms.topic: how-to +keywords: windows 10, windows 11, msix, psf, package support framework, process monitor, procmon, trace fixup +--- + +# Step 1: Identify compatibility issues in an MSIX container + +This is the first step in the [Package Support Framework (PSF) workflow](package-support-framework.md). +Before you can choose a runtime fix, you need evidence of what the application actually does at +runtime and which call fails. + +An MSIX package installs its files under `%ProgramFiles%\WindowsApps`, a location that grants the +application read and execute access but not write access. A packaged desktop application also starts +with `%windir%\System32` as its working directory, rather than the folder that holds the application +executable, unless the launch sets a different working directory. A 32-bit process sees the +`SysWOW64` directory instead, because of WOW64 file system redirection. Applications that were +designed for a traditional installer often depend on behavior that no longer holds in this +environment. + +## Prerequisites + +- Your desktop application packaged as an MSIX package, signed, and installed on the test machine. + For more information, see [Create an MSIX package](../packaging-tool/create-an-msix-overview.md). +- [Process Monitor](/sysinternals/downloads/procmon) from Sysinternals. +- Optional: [DebugView](/sysinternals/downloads/debugview) from Sysinternals, if you plan to use the + Trace Fixup. + +## Reproduce the failure + +Install the MSIX package, run the application, and record what you observe: + +- The exact error text or dialog, and whether it appears at launch or during a specific task. +- The activation path that reproduces the failure, such as a Start menu shortcut, a taskbar shortcut, + an execution alias, a file type association, or a protocol activation. A package can declare more + than one application, and each one needs its own entry in *config.json* in + [Step 3](psf-apply-a-runtime-fix.md). +- Whether the failure happens for every user or only for the user who installed the package. +- Whether the application recovers, hangs, or terminates. + +Error messages alone rarely identify the failing call, so capture the behavior with Process Monitor +next. + +## Use Process Monitor to identify an issue + +[Process Monitor](/sysinternals/downloads/procmon) observes the file system, registry, and process +operations of a running application and reports the result of each operation. After you open Process +Monitor, add a filter (**Filter** > **Filter…**) that includes only events from your application's +process. + +![Process Monitor filter for the application process](images/procmon_app_filter.png) + +If your application starts other processes, add a filter for those process names too. The failure +often happens in a child process, such as a helper executable or an updater, rather than in the +process that the shortcut starts. + +A list of events appears. For many of these events, the word **SUCCESS** appears in the **Result** +column. + +![List of Process Monitor events](images/procmon_events.png) + +Optionally, you can filter the events to show only failures. + +![Process Monitor filter that excludes successful results](images/procmon_exclude_success.png) + +If you suspect a file system access failure, look for failed events under either the +`System32`/`SysWOW64` directory or the package file path. Start at the bottom of the list and scroll +upward, because the failures at the bottom occurred most recently. Pay the most attention to results +such as **ACCESS DENIED**, **NAME NOT FOUND**, and **PATH NOT FOUND**. + +The [PSFSample](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/samples/PSFSample) +application has two issues, both visible in the following capture. + +![Process Monitor showing a failed read of Config.txt](images/procmon_config_txt.png) + +In the first issue, the application fails to read the *Config.txt* file from the +`C:\Windows\SysWOW64` path. The application is unlikely to reference that path directly. It's more +likely reading the file through a relative path, and `System32`/`SysWOW64` is the default working +directory of the process. That behavior indicates the application expects its working directory to +be set to a location in the package. Looking inside the package confirms that the file is installed +in the same directory as the executable. + +![Config.txt in the package next to the application executable](images/psfsampleapp_config_txt.png) + +The second issue appears in the following capture. + +![Process Monitor showing a failed write of a log file](images/procmon_logfile.png) + +Here, the application fails to write a *.log* file to its package path, which points to a file +redirection fix. + +> [!TIP] +> Two failures account for most Package Support Framework cases, and each has a focused +> walkthrough that starts with the Process Monitor capture: +> +> - [Package Support Framework - Working Directory fixup](psf-current-working-directory.md), when the +> application can't find files it ships with. +> - [How to fix Package Support Framework Filesystem Write Permission errors](psf-filesystem-writepermission.md), +> when the application writes to its install directory. + +## Use the Trace Fixup + +The Trace Fixup is an alternative diagnostic technique. Despite its name, it doesn't change runtime +behavior. It uses the same interception technology as the runtime fixes to report which function the +application called, which module called it, and whether the call succeeded. Because it reports at +the function level, it can identify issues that a kernel-level tool such as Process Monitor reports +only coarsely, and it can validate that another fixup is behaving as expected. + +The Trace Fixup reports only the functions that the PSF intercepts. If the application calls an API +that no fixup targets, the Trace Fixup doesn't report it, so use Process Monitor as well rather than +concluding from a quiet trace that nothing failed. + +The Trace Fixup source is in the +[tests/fixups/TraceFixup](https://github.com/microsoft/MSIX-PackageSupportFramework/tree/main/tests/fixups/TraceFixup) +directory of the Package Support Framework repository, and the built binaries, *TraceFixup32.dll* and +*TraceFixup64.dll*, are in the */bin* folder of the **Microsoft.PackageSupportFramework** NuGet +package. + +To use it, add the DLL to your package, add the following fragment to the `fixups` array of your +*config.json* file, and then package and install the application. List the Trace Fixup last in the +`fixups` array. The fixup that's listed last reports what the application itself requests, rather +than the calls that the other fixups make. + +```json +{ + "dll": "TraceFixup.dll", + "config": { + "traceLevels": { + "filesystem": "allFailures" + } + } +} +``` + +By default, the Trace Fixup filters out failures that it considers expected. For example, an +application might try to delete a file unconditionally without checking whether the file exists, and +ignore the result. Filtering has the unfortunate consequence that some unexpected failures are +filtered out too, so the preceding example opts in to all file system failures. That's useful here +because the attempt to read *Config.txt* fails with "file not found," a result that's frequently +observed and not generally treated as unexpected. In practice, start by filtering to unexpected +failures only, and fall back to all failures if an issue still can't be identified. + +By default, the Trace Fixup sends its output to the attached debugger with `OutputDebugString`. The +following captures show the output in [DebugView](/sysinternals/downloads/debugview), which displays +the same two failures that Process Monitor reported, pointing to the same runtime fixes. + +![DebugView output showing a file not found result](images/traceshim_filenotfound.png) + +![DebugView output showing an access denied result](images/traceshim_accessdenied.png) + +For the full set of trace options, including the other trace methods and the option to break into the +debugger on a failure, see the +[Trace Fixup readme](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/tests/fixups/TraceFixup/readme.md). + +## Record what you found + +Before you move to the next step, write down the following for each failure: + +- The function or operation that failed, and its result, such as **ACCESS DENIED**. +- The full path or registry key the application requested. +- Whether the path is package-relative, user-relative, or a system location. +- The name of the process that failed, and whether it's the process that the shortcut starts or a + child process. +- Whether the process that failed is 32-bit or 64-bit, which determines the Package Support Framework + binaries you add to the package in [Step 3](psf-apply-a-runtime-fix.md). + +## Next step + +> [!div class="nextstepaction"] +> [Step 2: Find a runtime fix](psf-find-a-runtime-fix.md) + +## Related content + +- [Package Support Framework overview](package-support-framework-overview.md) +- [Get started with the Package Support Framework](package-support-framework.md) +- [Automated PSF config generation with the MSIX Packaging Tool](psf-integration-with-mpt.md) diff --git a/msix-src/psf/run-scripts-with-package-support-framework.md b/msix-src/psf/run-scripts-with-package-support-framework.md index bcf85122..330e1ee6 100644 --- a/msix-src/psf/run-scripts-with-package-support-framework.md +++ b/msix-src/psf/run-scripts-with-package-support-framework.md @@ -34,11 +34,17 @@ Here are the locations of each executable. For more information about PowerShell execution policies, see [this article](/powershell/module/microsoft.powershell.core/about/about_execution_policies?preserve-view=true). -🚩 Make sure to also include the __StartingScriptWrapper.ps1__ file in your package and place it in the same folder as your executable. You can copy this file from the [PSF NuGet package](https://www.nuget.org/packages/Microsoft.PackageSupportFramework/) or from the [PSF Github repo](https://github.com/Microsoft/MSIX-PackageSupportFramework/releases). +> [!IMPORTANT] +> Include the `StartingScriptWrapper.ps1` file in your package. Place it in the same folder as the +> PSF launcher executable, which is normally the package root. The PSF launcher runs this wrapper +> for every start and end script. It looks for the wrapper next to itself first. In release +> 1.0.220926.1 and later, it then searches the rest of the package. You can copy the file from the +> [PSF NuGet package](https://www.nuget.org/packages/Microsoft.PackageSupportFramework/) or the +> [Package Support Framework repository](https://github.com/microsoft/MSIX-PackageSupportFramework/blob/main/PsfLauncher/StartingScriptWrapper.ps1). ## Enable scripts -To specify what scripts will run for each packaged application executable, you need to modify the [config.json file](package-support-framework.md#create-a-configuration-file). To tell PSF to run a script before the execution of the packaged application, add a configuration item called `startScript`. To tell PSF to run a script after the packaged application finishes add a configuration item called `endScript`. +To specify what scripts will run for each packaged application executable, you need to modify the [config.json file](psf-apply-a-runtime-fix.md#create-a-configuration-file). To tell PSF to run a script before the execution of the packaged application, add a configuration item called `startScript`. To tell PSF to run a script after the packaged application finishes, add a configuration item called `endScript`. ### Script configuration items diff --git a/msix-src/toc.yml b/msix-src/toc.yml index 658278a0..c5af47bc 100644 --- a/msix-src/toc.yml +++ b/msix-src/toc.yml @@ -159,6 +159,20 @@ href: psf/package-support-framework-overview.md - name: Get started with Package Support Framework href: psf/package-support-framework.md + - name: Step-by-step workflow + items: + - name: "Step 1: Identify compatibility issues" + href: psf/psf-identify-issues.md + displayName: "Process Monitor, procmon, Trace Fixup, diagnose" + - name: "Step 2: Find a runtime fix" + href: psf/psf-find-a-runtime-fix.md + displayName: "fixup, File Redirection Fixup, RegLegacyFixups, EnvVarFixup" + - name: "Step 3: Apply a runtime fix" + href: psf/psf-apply-a-runtime-fix.md + displayName: "config.json, PSF launcher, makeappx, repackage" + - name: "Step 4: Debug or extend a runtime fix" + href: psf/psf-debug-a-runtime-fix.md + displayName: "Visual Studio, WinDbg, debug fixup" - name: Create a Package Support Framework fixup href: psf/create-package-support-framework.md - name: Automated PSF config generation