如何修复包支持框架文件系统写入权限错误

本文介绍如何使用包支持框架 (PSF) 解决文件系统写入权限错误。

Windows 应用会将与特定应用程序相关的目录重定向到 C:\Program Files\WindowsApps 文件夹。 如果应用程序尝试写入 Windows 应用容器,将触发错误,且写入失败。 你可以对 Windows 应用包进行增强来解决此问题。

调查

首先,识别失败以及应用请求的目录路径。

捕获 Windows 应用失败

筛选结果是可选的,但更易于查看与应用程序相关的失败。 要筛选结果,请创建两个筛选规则。 第一个筛选器包含应用程序进程名称,第二个筛选器包含不成功的任何结果。

  1. 下载 SysInternals Process Monitor 并将其提取到 C:\PSF\ProcessMonitor 目录。

  2. 打开 Windows 资源管理器并导航到提取的 SysInternals ProcessMonitor 文件夹。

  3. 选择 SysInternals Process Monitor procmon.exe 文件以启动应用。

  4. 如果 UAC 提示,请选择“是”

  5. 在“进程监视器筛选器”窗口中,从第一个字段的下拉菜单中选择“进程名称”

  6. 验证“是”显示在下一个字段中。

  7. 在下一个字段中,输入应用的进程名称,例如 PSFSample.exe

    带有应用名称的“进程监视器筛选器”窗口的示例。

  8. 选择 添加

  9. 在“进程监视器筛选器”窗口中,从第一个字段的下拉菜单中选择“结果”

  10. 在下一个字段中,从下拉菜单中选择“不是”

  11. 在文本字段中,输入 SUCCESS

    包含结果的“进程监视器筛选器”窗口的示例。

  12. 依次选择“添加”、“确定” 。

  13. 启动 Windows 应用,触发错误,然后关闭 Windows 应用。

查看 Windows 应用失败日志

捕获 Windows 应用进程后,调查结果以确定失败是否与工作目录相关。

查看 SysInternals Process Monitor 失败结果。 如果结果包括“访问被拒绝”,且显示“所需的访问地:泛型写入”详细信息,则针对定位 C:\Program Files\WindowsApps\...\ 的应用,你发现了与工作目录相关的写入权限失败。

显示 SysInternals 进程监视器中的无法写入目录的错误消息。

如果识别此错误,请对你的应用程序应用以下 PSF 更正。

解决方法

要解决 Windows 应用无法写入 Windows 应用容器的问题,请执行以下步骤:

  1. 将 Windows 应用的内容提取到本地暂存目录
  2. 创建 config.json 并注入 PSF 修复文件到暂存 Windows 应用目录。
  3. 将应用程序启动器配置为指向 PSF 启动器,并将 PSF config.json 文件配置为重定向 PSF 启动器,以指定工作目录。
  4. 更新 Windows 应用 AppxManifest 文件
  5. 重新打包 Windows 应用并进行签名

下载并安装所需的工具

此流程需要以下工具:

  • NuGet 客户端工具
  • 包支持框架 (PSF)
  • Windows 10 软件开发工具包 (Win 10 SDK),最新版本
  • SysInternals Process Monitor

下载并安装 NuGet 和 PSF:

  1. 下载 NuGet 客户端工具的最新非预览版本,并将 nuget.exe 保存在 C:\PSF\nuget 中。

  2. 通过“管理 PowerShell”窗口运行以下命令,使用 Nuget 下载并安装包支持框架:

    Set-Location "C:\PSF"
    .\nuget\nuget.exe install Microsoft.PackageSupportFramework
    

下载并安装 Windows 10 SDK:

  1. 下载 Win 10 SDK
  2. 运行 winsdksetup.exe
  3. 选择下一步
  4. 仅选择以下三项功能:
    • 适用于桌面应用的 Windows SDK 签名工具
    • 适用于 UWP C++ 应用的 Windows SDK
    • 用于 UWP 应用本地化的 Windwos SDK
  5. 选择“安装”,然后选择“确定”

暂存 Windows 应用

暂存 Windows 应用会将应用的内容提取并解压缩到本地目录。 将 Windows 应用解压缩到暂存位置后,可以注入 PSF 修复文件以更正任何不需要的体验。

  1. 在“管理 PowerShell”窗口中,设置以下变量以定位特定应用文件和 Windows 10 SDK 版本:

    $AppPath          = "C:\PSF\SourceApp\PSFSampleApp.msix"         ## Path to the MSIX App Installer
    $StagingFolder    = "C:\PSF\Staging\PSFSampleApp"                ## Path to where the MSIX App will be staged
    $OSArchitecture   = "x$((gwmi Win32_Processor).AddressWidth)"    ## Operating System Architecture
    $Win10SDKVersion  = "10.0.19041.0"                               ## Latest version of the Win10 SDK
    
  2. 通过运行以下 PowerShell cmdlet 将 Windows 应用解压缩到暂存文件夹:

    ## Sets the directory to the Windows 10 SDK
    Set-Location "${env:ProgramFiles(x86)}\Windows Kits\10\Bin\$Win10SDKVersion\$OSArchitecture"
    
    ## Unpackages the Windows app to the staging folder
    .\makeappx.exe unpack /p "$AppPath" /d "$StagingFolder"
    

创建和注入所需的 PSF 文件

要更正 Windows 应用,请创建 config.json 文件,并提供有关失败的 Windows 应用启动器的信息。 如果多个 Windows 应用启动器遇到问题,你可以使用多个条目配置 config.json 文件。

创建 config.json 文件后,将 config.json 文件和支持的 PSF 修复文件移动到 Windows 应用包的根目录中。

  1. 打开 Visual Studio Code 或其他文本编辑器。

  2. 在 Windows 应用暂存目录 C:\PSF\Staging\PSFSampleApp 中创建名为 config.json 的新文件。

  3. 将以下代码复制到新建的 config.json 文件中。

    {
        "applications": [
            {
                "id": "",
                "executable": ""
            }
        ],
        "processes": [
            {
                "executable": "",
                "fixups": [
                {
                    "dll": "",
                    "config": {
                        "redirectedPaths": {
                            "packageRelative": [
                                {
                                    "base": "",
                                    "patterns": [
                                        ""
                                    ]
                                }
                            ]
                        }
                    }
                }
            ]
            }
        ]
    }
    
  4. 在 Windows 应用暂存文件夹中打开 AppxManifest.xml 文件。 以下示例显示 AppxManifest.xml 文件:

    <Applications>
        <Application Id="PSFSAMPLE" Executable="VFS\ProgramFilesX64\PS Sample App\PSFSample.exe" EntryPoint="Windows.FullTrustApplication">
        <uap:VisualElements BackgroundColor="transparent" DisplayName="PSFSample" Square150x150Logo="Assets\StoreLogo.png" Square44x44Logo="Assets\StoreLogo.png" Description="PSFSample">
            <uap:DefaultTile Wide310x150Logo="Assets\StoreLogo.png" Square310x310Logo="Assets\StoreLogo.png" Square71x71Logo="Assets\StoreLogo.png" />
        </uap:VisualElements>
        </Application>
    </Applications>
    
  5. config.json 文件中进行以下更改:

    • applications.id 值设置为与 AppxManifest.xml 文件的 Applications.Application.ID 字段中的值相同的值。

      显示 ID 在 AppxManifest 文件中位置的图像。

    • 设置 applications.executable 值以定位位于 AppxManifest.xml 文件的 Applications.Application.Executable 字段中的应用程序的相对路径。

      显示 *AppxManifest.xml* 文件中可执行文件位置的图像。

    • 设置 applications.workingdirectory 值以定位位于 AppxManifest.xml 文件的 Applications.Application.Executable 字段中的相对文件夹路径。

      显示 AppxManifest 文件中工作目录位置的图像。

    • AppxManifest.xml 文件的 process.executable 字段中设置 Applications.Application.Executable 值以定位文件名(不带路径和扩展名)。

      显示 AppxManifest 文件中进程可执行文件位置的图像。

    • 设置 processes.fixups.dll 值以定位体系结构特定的 FileRedirectionFixup.dll。 如果更正适用于 x64 体系结构,请将该值设置为 FileRedirectionFixup64.dll。 如果体系结构为 x86 或未知,请将该值设置为 FileRedirectionFixup86.dll

    • processes.fixups.config.redirectedPaths.packageRelative.base 值设置为 AppxManifest.xml 文件的 Applications.Application.Executable 字段中的包相对文件夹路径。

      显示 AppxManifest 文件中工作目录位置的图像。

    • 设置 processes.fixups.config.redirectedPaths.packageRelative.patterns 值以匹配应用程序创建的文件类型。 如果你使用 .*\\.log,PSF 将重定向 processes.fixups.config.redirectedPaths.packageRelative.base 目录和子目录中的所有日志文件写入。

  6. 保存已更新的 config.json 文件。 以下示例显示了更新的 config.json 文件:

    {
        "applications": [
            {
            "id": "PSFSample",
            "executable": "VFS/ProgramFilesX64/PS Sample App/PSFSample.exe"
            }
        ],
        "processes": [
            {
                "executable": "PSFSample",
                "fixups": [
                    {
                        "dll": "FileRedirectionFixup64.dll",
                        "config": {
                            "redirectedPaths": {
                                "packageRelative": [
                                    {
                                        "base": "VFS/ProgramFilesX64/PS Sample App/",
                                        "patterns": [
                                            ".*\\.log"
                                        ]
                                    }
                                ]
                            }
                        }
                    }
                ]
            }
        ]
    }
    
  7. 将以下文件从应用程序可执行体系结构的包支持框架复制到暂存 Windows 应用的根目录。 可以在 .\Microsoft.PackageSupportFramework.\<Version>\bin 中找到这些文件。

    应用程序 (x64) 应用程序 (x86)
    PSFLauncher64.exe PSFLauncher32.exe
    PSFRuntime64.dll PSFRuntime32.dll
    PSFRunDll64.exe PSFRunDll32.exe
    FileRedirectionFixup64.dll FileRedirectionFixup64.dll

更新 AppxManifest

创建和更新 config.json 文件后,为 config.json 中包含的每个 Windows 应用启动器更新 Windows 应用的 AppxManifest.xml 文件。 AppxManifest.xml Applications现在必须定位与应用程序体系结构关联的 PSFLauncher.exe

  1. 在暂存 MSIX 应用文件夹 C:\PSF\Staging\PSFSampleApp 中打开 AppxManifest.xml
  2. 使用以下代码更新 AppxManifest.xml
    <Package ...>
    ...
    <Applications>
        <Application Id="PSFSample"
                    Executable="PSFLauncher32.exe"
                    EntryPoint="Windows.FullTrustApplication">
        ...
        </Application>
    </Applications>
    </Package>
    

重新打包应用程序

应用所有更正后,将 Windows 应用重新打包到 MSIX 中,并使用代码签名证书对其进行签名。

  1. 打开“管理 PowerShell”窗口。

  2. 设置以下变量:

    $AppPath          = "C:\PSF\SourceApp\PSFSampleApp_Updated.msix" ## Path to the MSIX App Installer
    $CodeSigningCert  = "C:\PSF\Cert\CodeSigningCertificate.pfx"     ## Path to your code signing certificate
    $CodeSigningPass  = "<Password>"                                 ## Password used by the code signing certificate
    $StagingFolder    = "C:\PSF\Staging\PSFSampleApp"                ## Path to where the MSIX App will be staged
    $OSArchitecture   = "x$((gwmi Win32_Processor).AddressWidth)"    ## Operating System Architecture
    $Win10SDKVersion  = "10.0.19041.0"                               ## Latest version of the Win10 SDK
    
  3. 通过运行以下 PowerShell cmdlet 重新打包暂存文件夹中的 Windows 应用:

    Set-Location "${env:ProgramFiles(x86)}\Windows Kits\10\Bin\$Win10SDKVersion\$OSArchitecture"
    .\makeappx.exe pack /p "$AppPath" /d "$StagingFolder"
    
  4. 通过运行以下 PowerShell cmdlet 对 Windows 应用进行签名:

    Set-Location "${env:ProgramFiles(x86)}\Windows Kits\10\Bin\$Win10SDKVersion\$OSArchitecture"
    .\signtool.exe sign /v /fd sha256 /f $CodeSigningCert /p $CodeSigningPass $AppPath