使用包标识进行调试

许多Windows API(推送通知、后台任务、共享目标、启动任务、Windows AI API)要求应用具有打包标识。 在开发过程中,你不希望每次测试时生成完整的 MSIX 安装程序 , winapp 会提供两个命令,以便实时提供应用标识。

将Visual Studio用于打包项目? 如果已对打包项目使用Visual Studio,则可能不需要 winapp 进行调试。 Visual Studio 已处理包注册、标识、AUMID 激活、调试器挂载和激活代码调试——所有操作均通过按下 F5 触发。 它还为高级方案提供 调试→其他调试目标→调试已安装的应用包 。 下面的工作流最适用于 VS Code 用户、基于终端的工作流和 VS 不本机打包的框架 (Rust、Flutter、Tauri、Electron、plain C++)。

两种方法: winapp runcreate-debug-identity

winapp run create-debug-identity
它所注册的内容 完整松散布局包(整个文件夹) 稀疏包 (单一可执行文件)
应用启动方式 由 winapp 启动(用于激活或执行的 AUMID 别名) 您自行启动可执行文件(通过命令行、集成开发环境等)
模拟 MSIX 安装 是 — 最接近生产行为 否 - 仅稀疏标识
文件保持原位 复制到 AppX 布局目录 是 — exe 停留在其原始路径上
身份范围 整个文件夹内容(exe、DLL、assets) 单个可执行文件
调试器友好 在启动后附加到 PID,或使用 --no-launch 然后通过别名启动 IDE 调试器直接运行 — exe 文件始终具有标识
控制台应用支持 --with-alias 在终端中保留 stdin/stdout 直接在终端中运行 exe
最适用于 大多数框架(.NET、C++、Rust、Flutter、Tauri) Electron,或者当您需要完整的 IDE 调试器控制(F5)时

何时使用哪一个

默认值:winapp run

使用winapp run进行大多数开发工作流。 它模拟实际的 MSIX 安装 — 你的应用获取在生产环境中相同的标识、功能和文件关联。

# Build your app, then:
winapp run .\build\output

在以下情况下使用 create-debug-identity

  • 你的可执行文件(exe)与生成产物分离——例如,Electron 应用程序,其中 electron.exe 位于 node_modules/
  • 你需要调试启动代码 ,并且无法在 AUMID 启动后快速附加调试器
  • 对于某些无法通过 AUMID 启动的调试器来说,需要为启动的进程提供标识来注册 exe ,create-debug-identity 这样无论它如何启动,都具有标识。
  • 你正在专门测试稀疏包行为(AllowExternalContent,TrustedLaunch)
# Register identity for an exe, then launch it however you want:
winapp create-debug-identity .\bin\Debug\myapp.exe
.\bin\Debug\myapp.exe   # or F5 in your IDE

调试场景

场景 A:直接使用身份运行

最简单的工作流 - 生成,使用标识运行,完成。

winapp run .\build\Debug

Winapp 将文件夹注册为松散布局包并启动应用。 需要标识的 API 会立即工作。 这包括大多数开发和测试方案。

对于当前终端中需要 stdin/stdout 的 控制台应用 ,请添加 --with-alias

winapp run .\build\Debug --with-alias

方案 B:将调试器附加到正在运行的应用

使用 winapp run 启动,记录 PID,然后连接 IDE 的调试器。

winapp run .\build\Debug
# Output: Process ID: 12345

然后在 IDE 中:

  • VS Code:运行和调试→选择“附加”配置(请参阅以下 IDE 设置
  • WinDbgwindbg -p 12345

限制: 在附加之前,你将错过运行的任何代码。 对于启动调试,请使用方案 D (create-debug-identity)。

方案 C:注册标识,然后通过 IDE 的 AUMID 或别名启动

使用 --no-launch 注册包,然后通过 IDE 中 AUMID(由 run 报告)或 执行别名 启动应用程序。

步骤 1: 注册包而不启动:

winapp run .\build\Debug --no-launch

步骤 2: 将 IDE 配置为通过 AUMID 或 执行别名 (而不是直接执行)启动。

  • 使用 AUMID 启动:使用命令 start shell:AppsFolder\<AUMID>winapp run 在注册应用时输出 AUMID。
  • 使用别名启动:必须在清单中定义别名(Package.appxmanifest 首选, appxmanifest.xml 也受支持)。

重要: 在生成文件夹中启动 exe 不会 为其提供标识。 应用必须通过 AUMID 激活或其执行别名启动。 这就是松散布局包的工作方式 - 标识与激活路径(而不是 exe 文件)相关联。

方案 D:使用标识从 IDE 启动(启动调试)

这是 使用完全 IDE 控件调试启动代码 的最佳方法 - IDE 的调试器从第一个指令控制进程,无论启动方式如何,exe 都有标识。

winapp create-debug-identity .\build\Debug\myapp.exe

现在,从终端、VS Code 的 F5、脚本中以任意方式启动 exe。 exe 具有标识,因为Windows注册了 sparse 包直接指向它。

它与 winapp run 的区别在于: 使用 create-debug-identity,标识依靠 exe 本身(借助 Add-AppxPackage -ExternalLocation)。 使用 winapp run时,标识绑定到松散的布局包 - 应用必须通过 AUMID 或别名启动。 当您需要 IDE 直接启动和调试 exe 时,create-debug-identity 是更佳的选择。

这也是 电子应用 的最佳方法,其中 exe 路径与源目录不同。

方案 E:捕获调试输出和故障诊断

内联捕获OutputDebugString消息和首次机会异常。 框架噪声(WinUI、COM、DirectX 内部跟踪)被从控制台中筛除,因此仅显示应用的调试消息。 所有内容仍会写入日志文件,以便进行全面调查。

如果应用崩溃,将自动捕获和分析小型内存转储:

winapp run .\build\Debug --debug-output

在崩溃时,输出包括异常类型、消息以及堆栈跟踪,其中包含的源文件和行号是从生成输出文件夹中的 PDB 解析的。 可以在没有外部工具的情况下,立即分析托管代码(.NET)崩溃。 本机 (C++/WinRT) 崩溃时显示模块名称和偏移量;添加 --symbols 来下载完整函数名称的 PDB 符号:

winapp run .\build\Debug --debug-output --symbols

重要: 这会附加 winapp 作为调试器。 Windows仅允许每个进程一个调试器,因此无法同时附加Visual Studio、VS Code 或 WinDbg。

IDE 设置

VS Code

WinApp VS Code 扩展提供了一种自定义的winapp调试类型,它通过包身份启动应用并附加调试器,只需按一下F5

使用身份进行一键 F5 调试

winapp 启动配置添加到 .vscode/launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "winapp",
            "request": "launch",
            "name": "WinApp: Launch and Attach"
        }
    ]
}

F5 时:

  1. 该扩展将扫描工作区以查找包含 .exe 文件的生成输出目录。
  2. 选择要运行的生成文件夹(或设置为 inputFolder 跳过提示)。
  3. 通过 winapp run 启动你的应用以提供其程序包标识。
  4. 子调试会话使用指定的调试器附加到正在运行的进程。

调试器附加后,你将获得完整的 VS Code 调试体验 - 通过单击编辑器的行号区域设置断点,逐行单步执行代码 (F10)、单步进入函数 (F11)、在变量窗格中检查变量,并在调试控制台中计算表达式。 应用在整个过程中都使用包标识运行,因此依赖于标识的 API 的行为与生产中的行为完全相同。

重要: 调试 winapp 类型 不会 自动生成项目。 进行代码更改后,在按 F5 之前重新生成。

使用 preLaunchTask 自动化构建

为了避免忘记重新构建项目,请在每次调试会话之前添加一个 preLaunchTask 来构建项目。

  1. .vscode/tasks.json 中定义生成任务(例如.NET):
    {
        "version": "2.0.0",
        "tasks": [
            {
                "label": "build",
                "command": "dotnet",
                "type": "process",
                "args": ["build", "${workspaceFolder}"],
                "problemMatcher": "$msCompile"
            }
        ]
    }
    
  2. launch.json中引用此内容。
    {
        "type": "winapp",
        "request": "launch",
        "name": "WinApp: Launch and Attach",
        "preLaunchTask": "build"
    }
    

配置属性

财产 类型 违约 Description
inputFolder 字符串 包含您的应用程序二进制文件的生成输出文件夹的路径(例如,${workspaceFolder}/bin/Debug/net8.0-windows10.0.22621)。 如果未设置,系统会提示你选择文件夹。
manifest 字符串 AppX 清单文件的路径(例如,AppxManifest.xmlPackage.appxmanifestappxmanifest.xml)。 如果未设置,CLI 将从输入文件夹或当前目录自动检测。
debuggerType 字符串 coreclr 要使用的基础调试器(coreclrcppvsdbgnode)。
workingDirectory 字符串 工作区文件夹 应用程序的工作目录。
args 字符串 要传递给应用程序的命令行参数。
outputAppxDirectory 字符串 松散布局包的输出目录。 默认为输入文件夹中的文件夹 AppX
port number 9229 node 仅) 用于 Node.js --inspect 侦听器和附加连接的端口。 当默认端口已在使用时重写。

支持的调试器

debuggerType 语言 所需的扩展
coreclr(默认值) C# / .NET C# 开发工具包
cppvsdbg C/C++ C/C++
node Node.js/Electron 内置

C++ 项目的示例:

{
    "type": "winapp",
    "request": "launch",
    "name": "WinApp: Launch C++ App",
    "debuggerType": "cppvsdbg"
}

使用创建调试标识启动调试

如果需要从首条指令开始调试启动代码,F5 按键附加方法可能会错过初始代码。 请使用命令面板中的WinApp: 创建调试标识命令为您的可执行文件注册一个稀疏包,然后用标准调试器启动它:

{
    "name": "Launch (with identity)",
    "type": "coreclr",
    "request": "launch",
    "program": "${workspaceFolder}/bin/Debug/net8.0-windows10.0.22621/myapp.exe"
}

由于 create-debug-identity 在 exe 本身上注册了标识,因此应用无论如何启动都具有标识,包括通过标准的 VS Code 启动配置启动。

附加到正在运行的进程

如果您更喜欢从终端启动 winapp run 然后附加,请使用标准的附加配置。

{
    "name": "Attach to Process",
    "type": "coreclr",
    "request": "attach",
    "processId": "${command:pickProcess}"
}

对于 C++/Rust,请使用 "type": "cppvsdbg" (MSVC) 或 "type": "lldb" (LLDB):

{
    "name": "Attach (C++)",
    "type": "cppvsdbg",
    "request": "attach",
    "processId": "${command:pickProcess}"
}

清理

完成测试后,请从命令面板运行 WinApp:注销包 ,以删除旁加载的开发包,而无需离开 VS Code。