WSL2 通过 vscode 调试 native(ELF) 格式的 dotnet 程序(瞎折腾)
先说结论:参见 launch.json 中的注释部分,引用了 cef 的 dotnet 程序在 Linux 下面是 vscode 调试不了的,不引用 cef 的话 VS2022 在 WSL 模式下就能调试,所以不用折腾了!
默认情况下,VS2022 调试运行是通过 dotnet MyApp.dll 启动目标程序的,当调试对象是包含引用 cef 之类需要多进程执行的应用程序时,VS2022 能自动附加到 CEF 子进程(Windows 特有机制),
1. CEF 启动时会创建多个子进程: Browser 进程(你的主程序) Renderer 进程(渲染网页内容) GPU / Utility / Plugin 等进程 2. VS2022 默认启用“子进程调试” 项目属性 → Debug → 勾选了 "Enable native code debugging" 或 "Debug child processes" 当主进程启动 CEF 子进程时,VS2022 会自动附加调试器到所有子进程 即使子进程是 chrome.exe 或 MyApp.exe --type=renderer,也能被监控 3. Windows 的调试 API 支持进程继承 Windows 提供 DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS 标志 VS2022 启动主进程时使用 DEBUG_PROCESS,自动继承调试权限到子进程 所有子进程的异常、断点、模块加载都会通知调试器
但是 WSL2 下的 Linux 调试工具链没有这种支持。当子进程命令是 "./MyApp --type=renderer" 时,后台启动的"dotnet --type=renderer ..." 会被视作非法命令,导致目标进程崩溃,即使禁用 CEF 多进程:--single-process 也不行。
这就导致明明引用了 CEF 的 dotnet 应用程序发布成 linux-x64 后可以运行,但 VS2022 却无法在 WSL 模式下调试启动它。以往的解决办法是发布成 linux-x64 以后,通过 VS2022 + SSH 在 Linux 主机上远程调试,比较麻烦,官方已经不推荐了。如今更主流的做法,是使用 vscode 在 WSL2 中实现 dotnet 程序的 native 调试。
1. 在 WSL2 中进入解决方案或工程项目的目录,启动 vscode (注意,要在宿主 Windows 系统上安装了 vscode,不要在 WSL2 里直接安装)
code .
2. 手动创建 .vscode/launch.json 或者在 vscode 中“添加配置”
"version": "0.2.0",
"configurations": [
{
"name": "Debug Native App",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/${input:projectName}/bin/Debug/net8.0/${input:projectName}",
"cwd": "${workspaceFolder}/${input:projectName}",
"console": "integratedTerminal"
// ==== 以下内容,建议先看完再动手,因为其实不需要动手 ====
// 直接运行 native 程序,代替 dotnet *.dll 方式启动调试
//"console": "integratedTerminal",
//"stopAtEntry": true,
// "env": {
// "DISPLAY": ":0",
// "WAYLAND_DISPLAY": "", // 清空(Win10 WSLg 不支持 Wayland)
// "XDG_SESSION_TYPE": "x11", // 显式指定 X11
// "GDK_BACKEND": "x11", // 强制 GTK3 使用 X11
// "XDG_RUNTIME_DIR": "/run/user/0", // 替换为 vscode 终端运行的 UID, 用 id -u 查询,以 root 运行时是 0,普通用户运行一般是 1000
// "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/0/bus" // UID 要与 XDG_RUNTIME_DIR 设置一致
// }
//
// 但是由于 vsdbg 在 integratedTermina 模式下会直接 fork + 注入调试代理,可能干扰 X11/GTK 初始化
// 让 vscode 调用系统终端(如 xterm -e ...),通过终端再启动你的程序,确保 vsdbg 只 attach,不控制启动上下文
// 需要手动安装 xterm 程序:apt install xterm
//"console": "externalTerminal",
//
// 结果用 xterm 直接运行可以,但是调试启动也不灵,
// 尝试手动从内置控制台启动进程后,在 vscode 里面 Attach to Process 附加到进程调试
// 最好在 Program.Main() 中启动时通过 Debugger.IsAttached 判断并等待调试器附加
//
// 然而不幸的是,cef 在 linux 下调试器一附加,就 Trace/breakpoint trap (core dumped) 退出了
// 结论:尽量在 Windows 下调试,Linux 下还是死心踏地祭出 Console.WriteLine 大法,放弃挣扎吧
}
],
"inputs": [
{
"id": "projectName",
"type": "pickString",
"description": "请指定要以 Native 模式调试的工程",
"options": ["MyApp"],
"default": "MyApp"
}
]
}
因为 dotnet build 在 WSL2 中除了生成 MyApp.dll 之外,还同时生成了 MyApp 这个 ELF 格式的可执行程序(对应 Windows 下生成 PE 格式的 MyApp.exe),所以直接指定运行这个可执行文件,可以代替执行默认的 dotnet MyApp.dll。注意这里将 .vscode 目录创建在解决方案目录下的,所以增加了一个选择调试目标工程的步骤。如果是在工程目录下创建的,那么可以去掉 program 和 cwd 路径中的 /${input:projectName},把 program 中的第二个 /${input:projectName} 换成实际的 MyApp 即可。
3. 保存 launch.json 后,在 vscode 中菜单“运行”——“启动调试(F5)” ,你会发现应用程序如愿停在了 vscode 中打了断点的位置。
综上所述,当你在 Linux 或 WSL2 下开发 dotnet + CEF 这类需要调试运行 native executable 的场景,vscode 是更灵活、更合适的选择。
折腾半天,被 DeepSeek 和 Qwen 耍得团团转,有道是“AI得来终觉浅,绝知此事要躬行”
补充说明:修改 csproj 文件,在 PropertyGroup 中添加以下内容,可以让 WSL 下通过控制台运行更方便一点
<!-- 指定工程编译 linux-x64 自包含应用,即除了生成默认的 .NET 程序集(assembly)VMP.Container.dll 以外, Debug/Release 直接生成 Linux ELF 格式的可执行文件,而非 Windows 可执行的 PE 格式 .exe 文件, 这个文件是可以直接在 WSL 的控制台中启动运行的,免去了每次要发布成 linux-x64 才能在 Linux 运行的麻烦。 --> <RuntimeIdentifier>linux-x64</RuntimeIdentifier> <!-- 是否自动在 OutputPath 中加入 RuntimeIdentifier false 不将 RID 表示添加到输出路径中, 将生成文件到 ${BaseOutputPath}\$(Configuration)\$(TargetFramework)\, 即生成 bin\GtkForm.Linux\Debug\net8.0\VMP.Container true 将生成文件到 ${BaseOutputPath}\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier), 即生成 bin\GtkForm.Linux\Debug\net8.0\linux-x64\VMP.Container --> <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>

浙公网安备 33010602011771号