VS Code C++ 调试报错:preLaunchTask exit code -1 的排查与解决

今天在 VS Code 里写洛谷 P1126《机器人搬重物》,代码写完之后准备按 F5 调试,结果还没开始调试,VS Code 先给我弹了一个错误:

1
The preLaunchTask 'C/C++: g++.exe build active file' terminated with exit code -1.

一开始我第一反应就是:

是不是代码又写崩了?

毕竟 P1126 本身就是 BFS,边界、数组、方向这些地方稍微写错一点,就很容易出问题。

但不对啊,我数据还没输呢,用hello world试了一下报同样的错。

整个过程比较曲折,中间还碰到了 chcp、tasks.json、launch.json、g++、gdb、VS Code 扩展等一堆东西。这里把整个过程记录下来,之后如果再碰到类似问题,也可以直接回来查。


一、刚开始的报错

按下 F5 后,VS Code 弹出:

err

1
The preLaunchTask 'C/C++: g++.exe build active file' terminated with exit code -1.

然后终端里面还有:

1
2
3
4
5
cmd /c chcp 65001>nul && D:\mingw64\bin\g++.exe -fdiagnostics-color=always -g D:\code_world\code_cpp\hello.cpp -o D:\code_world\code_cpp\hello.exe

'chcp' 不是内部或外部命令,也不是可运行的程序
Build finished with error(s).
The terminal process failed to launch (exit code: -1).

最引人注意的就是:

1
'chcp' 不是内部或外部命令

问题来了。

chcp 是 Windows 自带的命令,我直接打开 Windows 的 cmd 测试:

1
chcp

完全正常。

这就有点奇怪了:

1
2
3
4
5
6
7
Windows cmd
    ↓
chcp 正常

VS Code
    ↓
chcp 找不到

所以当时我就开始怀疑是 VS Code 的环境有问题。


二、先看看 g++ 和 gdb 有没有问题

既然怀疑环境,最简单的方法就是直接测试编译器。

在 VS Code 终端输入:

1
g++ --version

输出:

1
g++.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0

再测试 gdb:

1
gdb --version

输出:

1
GNU gdb (GDB) 8.1

这说明至少有两个东西是正常的:

1
2
g++
gdb

都能被找到。

所以暂时不用考虑重新安装 MinGW。


三、检查 tasks.json

接下来检查 .vscode/tasks.json。

我的配置大概是这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: g++.exe build active file",
            "command": "D:\\mingw64\\bin\\g++.exe",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${file}",
                "-o",
                "${fileDirname}\\${fileBasenameNoExtension}.exe"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Task generated by Debugger."
        }
    ],
    "version": "2.0.0"
}

看了一遍,没有发现什么明显问题。

尤其是:

1
"command": "D:\\mingw64\\bin\\g++.exe"

这里直接写的是 g++ 的绝对路径。

也就是说,即使 PATH 有问题,VS Code 也应该能够直接找到这个 g++。

所以:

tasks.json 暂时排除。


四、然后发现一个关键问题:没有 launch.json

继续检查 .vscode 文件夹,发现之前其实没有配置 launch.json。

这时候才意识到,我对 VS Code 的“编译”和“调试”其实一直没有完全搞清楚。

简单来说:

tasks.json 干什么?

负责:

1
怎么编译程序

比如:

1
2
3
调用 g++.exe
输入哪个 cpp
输出哪个 exe

launch.json 干什么?

负责:

1
怎么启动调试

比如:

1
2
3
启动哪个 exe
使用哪个 gdb
程序运行时的工作目录是什么

而 preLaunchTask 就是把两者连接起来。

可以简单理解成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
F5
 ↓
launch.json
 ↓
preLaunchTask
 ↓
tasks.json
 ↓
g++
 ↓
生成 exe
 ↓
gdb
 ↓
开始调试

所以我补了一个 launch.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "C/C++: g++.exe 调试活动文件",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}\\${fileBasenameNoExtension}.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "D:\\mingw64\\bin\\gdb.exe",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: g++.exe build active file"
        }
    ]
}

这里有一行特别重要:

1
"preLaunchTask": "C/C++: g++.exe build active file"

它必须和 tasks.json 里的:

1
"label": "C/C++: g++.exe build active file"

完全一致。


五、结果加了 launch.json 还是报错

本来以为这下应该好了。

结果按 F5:

1
The preLaunchTask 'C/C++: g++.exe build active file' terminated with exit code -1.

还是这个。

然后终端里依旧有:

1
cmd /c chcp 65001>nul && D:\mingw64\bin\g++.exe ...

紧接着:

1
'chcp' 不是内部或外部命令,也不是可运行的程序

这时候我基本确定:

这次的问题应该不是 C++ 代码,而是 VS Code 执行任务时的 shell 环境。


六、为什么 VS Code 会执行 chcp?

这里顺便查了一下 chcp。

chcp 是 Windows cmd 的一个命令,用来切换控制台代码页。

例如:

1
chcp 65001

就是切换到 UTF-8。

VS Code 的 C++ 构建任务在 Windows 下可能会生成类似这样的命令:

1
cmd /c chcp 65001>nul && g++ ...

也就是说:

1
2
3
先执行 chcp 65001
       ↓
再执行 g++

所以如果 chcp 出问题,VS Code 就可能把整个任务判定成失败。

问题也就变成了:

为什么 Windows 自己的 cmd 能执行 chcp,VS Code 里面却找不到?


七、开始检查 VS Code 的终端配置

当时我的 settings.json 里面配置了多个终端:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
"terminal.integrated.profiles.windows": {
    "PowerShell": {
        "source": "PowerShell",
        "icon": "terminal-powershell"
    },
    "Command Prompt": {
        "path": "C:\\WINDOWS\\System32\\cmd.exe",
        "args": [
            "/K",
            "chcp 65001"
        ]
    },
    "Git Bash": {
        "source": "Git Bash",
        "icon": "terminal-git-bash"
    },
    "PowerShell 7": {
        "path": "pwsh.exe",
        "icon": "terminal-powershell"
    }
},
"terminal.integrated.defaultProfile.windows": "PowerShell 7"

这里确实有点复杂。

我配置了:

  • PowerShell
  • PowerShell 7
  • Command Prompt
  • Git Bash

而且 Command Prompt 自己又加了:

1
/K chcp 65001

这就让我开始怀疑,是不是终端配置之间发生了冲突。

于是我把默认终端改成:

1
"terminal.integrated.defaultProfile.windows": "Command Prompt"

结果……

还是不行。

chcp 依然报错。


八、又尝试绕开 cppbuild

既然问题可能出在 VS Code 自动生成的:

1
cmd /c chcp 65001>nul && ...

那我就想:

能不能不让它自动生成?

于是尝试把:

1
"type": "cppbuild"

改成:

1
"type": "shell"

希望直接按照自己的命令执行。

结果还是一样。

后来还尝试过:

1
"terminal.integrated.inheritEnv": false

想让 VS Code 不继承主进程的环境变量。

但也没有解决。

这时候就有点尴尬了。

因为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
系统 cmd
    ↓
chcp 正常

VS Code
    ↓
chcp 不正常

g++
    ↓
正常

gdb
    ↓
正常

问题越来越像 VS Code 自己的环境了。


九、检查 VS Code 日志,又发现了一些奇怪东西

继续看 VS Code 的日志,发现里面有:

1
Failed to find the "go" binary in either GOROOT() or PATH

同时看到 VS Code 加载了大量扩展:

1
2
3
4
5
6
7
8
9
anthropic.claude-code
golang.go
ms-azuretools.vscode-containers
ms-dotnettools.csdevkit
ms-python.python
redhat.java
vscjava.vscode-gradle
xsro.masm-tasm
...

也就是说,这个 VS Code 并不是一个单纯的 C++ 开发环境。

Go、Java、Python、C#、Docker、Gradle、汇编等一大堆扩展都装了。

所以当时一个比较自然的猜测就是:

会不会某个扩展在启动的时候修改了 PATH?

这个方向确实值得怀疑。

不过后来回头看,这里最好不要直接下结论说“这些扩展一定污染了 PATH”。

因为仅仅看到扩展加载,并不能证明某一个扩展就是罪魁祸首。

更准确的说法应该是:

VS Code 内部的环境变量和 shell 配置与系统 cmd 存在差异,而扩展和 VS Code 配置都属于需要排查的对象。


十、最关键的一次测试:程序居然真的运行了

后来为了彻底搞清楚到底有没有编译成功,我没有再看 VS Code 弹窗,而是直接手动执行调试器。

类似:

1
D:\code_world\code_cpp> cmd /C "...WindowsDebugLauncher.exe ... --dbgExe=D:\mingw64\bin\gdb.exe --interpreter=mi "

结果程序正常输出:

1
Hello, world!

这一下就很有意思了。

因为这说明:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
g++
    ↓
编译成功

gdb
    ↓
启动成功

exe
    ↓
存在

程序
    ↓
正常运行

也就是说,之前看到的:

1
exit code -1

并不能简单理解为:

“C++ 编译失败了。”

至少在这次环境里,实际的编译和调试流程是可以跑起来的。

所以之前那个错误更像是:

VS Code 的任务系统因为 shell 环境中的 chcp 异常,把整个 preLaunchTask 判定成了失败。

这也是为什么我后来把它称为“干扰性报错”。


十一、真正把问题搞清楚,是因为我删了一行配置

后来排查的时候,我为了测试,直接把:

1
"preLaunchTask": "C/C++: g++.exe build active file"

删掉了。

然后再按 F5。

这次 VS Code 给出了一个完全不同的错误:

1
launch: program 'D:\code_world\code_cpp\luogu\P1126.exe' does not exist

这次就很好理解了。

因为没有 preLaunchTask:

1
2
3
4
5
6
7
8
9
F5
 ↓
不编译
 ↓
直接找 P1126.exe
 ↓
文件不存在
 ↓
报错

这也从侧面证明了:

preLaunchTask 确实是 F5 自动编译流程中的关键配置。

把它恢复:

1
"preLaunchTask": "C/C++: g++.exe build active file"

整个流程就恢复了。


十二、所以最终到底是什么问题?

现在回头看,整个问题其实是两个事情叠在了一起。

问题一:launch.json 的 preLaunchTask

这个是真正影响 F5 自动编译流程的关键配置。

如果没有:

1
"preLaunchTask": "C/C++: g++.exe build active file"

那么 F5 不会自动执行:

1
g++

而是直接尝试启动:

1
program

指定的 exe。

如果 exe 还没有生成,就会看到:

1
program ... does not exist

问题二:chcp 报错

另外还有一个:

1
'chcp' 不是内部或外部命令

它来自 VS Code 执行构建任务时的 shell 环境。

这部分容易把人带偏,因为它看起来特别像:

1
编译失败

但实际测试发现:

1
2
3
4
5
g++

gdb

exe

都是可以工作的。

所以这部分更像是 VS Code 任务系统中的环境问题,而不是 C++ 程序本身的问题。


十三、最后的配置

如果只是想让 VS Code 正常完成:

1
F5 → 编译 → 调试

可以使用下面这套配置。

tasks.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: g++.exe build active file",
            "command": "D:\\mingw64\\bin\\g++.exe",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${file}",
                "-o",
                "${fileDirname}\\${fileBasenameNoExtension}.exe"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

launch.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "C/C++: g++.exe 调试活动文件",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}\\${fileBasenameNoExtension}.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "D:\\mingw64\\bin\\gdb.exe",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: g++.exe build active file"
        }
    ]
}

终端部分可以先保持简单:

1
2
3
4
5
6
"terminal.integrated.profiles.windows": {
    "Command Prompt": {
        "path": "C:\\Windows\\System32\\cmd.exe"
    }
},
"terminal.integrated.defaultProfile.windows": "Command Prompt"

十四、以后遇到类似问题,可以这样查

如果以后 VS Code 又出现:

1
preLaunchTask ... exit code -1

不要第一时间去改 C++ 代码。

可以按照这个顺序检查。

第一步:直接编译

在终端执行:

1
g++ test.cpp -o test.exe

如果能生成:

1
test.exe

说明 g++ 基本没问题。


第二步:直接运行

1
test.exe

如果程序正常运行,说明:

1
2
3
代码
+
编译器

至少基本正常。


第三步:检查 gdb

1
gdb --version

确认调试器是否存在。


第四步:检查 tasks.json

重点看:

1
"command": "D:\\mingw64\\bin\\g++.exe"

以及:

1
"label": "C/C++: g++.exe build active file"

第五步:检查 launch.json

重点看:

1
"program": "${fileDirname}\\${fileBasenameNoExtension}.exe"

以及:

1
"miDebuggerPath": "D:\\mingw64\\bin\\gdb.exe"

最重要的是:

1
"preLaunchTask": "C/C++: g++.exe build active file"

第六步:确认两个名字完全一样

这两个地方:

1
2
// tasks.json
"label": "C/C++: g++.exe build active file"

和:

1
2
// launch.json
"preLaunchTask": "C/C++: g++.exe build active file"

必须一模一样。

哪怕少一个字符,F5 都无法正确找到对应的编译任务。


十五、这次踩坑最大的收获

这次问题让我比较深的一个感受就是:

VS Code 的报错信息不一定就是最终原因。

比如这次看到:

1
preLaunchTask ... exit code -1

很容易直接理解成:

1
g++ 编译失败

但实际上继续往下看才发现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
chcp
 ↓
shell 环境异常

g++
 ↓
其实可以工作

gdb
 ↓
其实也可以工作

exe
 ↓
实际上能够运行

所以以后看到类似错误,最好不要只盯着第一行红色报错。

把终端完整输出往下看,经常能发现真正有用的信息。


最后总结一下

这次的问题可以简单概括成:

1
2
3
4
5
6
7
F5
 ↓
VS Code 执行 preLaunchTask
 ↓
任务环境出现 chcp 异常
 ↓
VS Code 报 exit code -1

同时,如果:

1
launch.json

里面没有:

1
"preLaunchTask": "C/C++: g++.exe build active file"

那么 F5 又不会自动编译程序,就会进一步出现:

1
program xxx.exe does not exist

所以最终需要保证的核心配置就是:

1
"preLaunchTask": "C/C++: g++.exe build active file"

让整个流程重新变成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
按 F5
  ↓
执行 tasks.json
  ↓
g++ 编译
  ↓
生成 exe
  ↓
启动 gdb
  ↓
进入调试

至于 chcp 的问题,如果后面还频繁出现,可以再单独排查 VS Code 的 PATH、终端 profile 和扩展环境。

不要因为一个 exit code -1 就马上重装 MinGW,也不要一上来就怀疑自己。先把“代码、编译、调试、VS Code 任务、终端环境”一层一层拆开,定位起来会快很多。