
1. 为什么今天还有人在装 Visual Studio 2019Visual Studio 2019 这个老伙计在 Visual Studio 2022 已经铺开好几年的今天依然活跃在很多团队的生产环境里。我做过的几个项目里有的产线设备驱动工具链只认它有的老 .NET Framework 项目升级一次要动几百个文件最后大家一致决定留着 2019别折腾。它内部版本号是 16.xC 工具集是 MSVC v142支持到 .NET Core 3.1、.NET 516.8 之后以及 .NET Framework 4.8对 C14/C17 是完整支持的C20 也塞进去了一部分概念、协程在 16.3 之后陆续可用模块是预览状态。如果你是新入行的开发者可能会疑惑不是有更新的版本吗道理很简单——工具的价值不在于版本号而在于生态匹配度。Visual Studio 2019 的 SDK 版本矩阵、第三方插件兼容性、构建服务器CI上的镜像可用性都已经非常稳定了。很多企业的内网构建机镜像早在 2020 年前后就固化了 2019 的版本改一次要过一轮安全审计和回归测试成本不低。这篇内容我想聊的是实操层面的东西。适合谁看三类人一是接手老项目需要立刻跑起来的二是被编译编码问题折磨过的 C 或 C# 开发者三是在服务器上装 MySQL、跑 CMake、跑 node-gyp 时被找不到 Visual Studio报错拦住的人。我会把安装规划、UTF-8 编码设置、命令行构建、报错排查这几个高频场景拆开讲清楚包含我自己踩过的坑和当时没找到答案、后来才想明白的细节。1.1 Community、Professional、Enterprise 三个版本到底怎么选大多数人直接装 Community 就行它是免费的个人开发者、学术研究、小型团队我记得是五人以内的企业组织都可以合法使用。功能上它和 Professional 的差距主要在协作和高级调试方面日常写代码、调试、做单元测试完全够用。Professional 和 Enterprise 走的是订阅制。Professional 主要多了 CodeLens 的一些协作特性、更完整的架构验证工具。Enterprise 才是真正的全家桶——Live Unit Testing、IntelliTrace 快照调试、代码克隆检测、Fakes 隔离框架这些都在这一档。我个人的判断标准很粗暴如果你需要在不重启调试会话的情况下回看历史执行快照或者团队在做大型遗留系统的重构那 Enterprise 才有意义否则那笔订阅费花得不值。有一个容易被忽略的点三个版本可以共存安装在同一台机器上安装器会给它们分配不同的目录Community、Professional、EnterpriseDevEnv 的实例 ID 也是分开的。但我不建议这么做除非你确实在做跨版本兼容测试。原因是共享组件目录Shared会被多个版本争抢更新曾经出现过装完 Enterprise 之后 Community 的某个 SDK 被悄悄降级的情况排查起来很头疼。1.2 与 VS2017、VS2022 之间的取舍逻辑VS2017 和 VS2019 的 C 工具集是二进制兼容的v141 和 v142 在同一个 MSVC 兼容集 里链接的时候可以混用这是微软当年一个很聪明的设计。所以如果某个第三方静态库只有 2017 编译出来的版本用 2019 链接通常不会出问题。但反过来VS2022 的 v143 工具集和 v142 就不保证这种兼容了混链接大概率会撞到 CRT 版本冲突。VS2022 是 64 位进程这个变化带来的实际好处是大型解决方案几千个 C 源文件的 IntelliSense 不再动不动就内存爆掉。而 VS2019 的 IDE 主体是 32 位程序物理上装在Program Files (x86)目录下处理超大工程时确实会喘。但它换来了兼容性——VS2019 能跑在 Windows 7 SP1 及以上系统VS2022 的官方最低要求是 Windows 10 1909。如果你的构建机还挂着 Windows Server 2012 R2那基本没有选择余地。还有一类场景是 SDK 锁定。比如某些厂商的板级支持包、仿真器插件、老版本的 CUDA 工具链它们的安装脚本会去注册表里找VisualStudio\2019这个键。这种时候装新版本没用脚本找不到就是找不到。所以我的建议是新项目直接上 2022老项目原地不动需要跨版本时用vcvarsall.bat切换环境而不是反复卸载重装。2. 安装规划装什么、不装什么这步错了后面全是坑Visual Studio 2019 的安装体积可以从 5GB 到 60GB 不等差距全在你勾了哪些工作负载。我见过最夸张的一台机器工程师把移动开发游戏开发数据存储和处理全勾上装完 C 盘直接少了 80 个 G结果项目只用到 C 桌面开发。所以安装这一步值得花二十分钟认真规划。另外一个常见误区是先全装用不到再说。Visual Studio 的工作负载不是简单堆文件它会注册大量的项目模板、SDK 探测路径、MSBuild 目标文件。装得越多项目属性的下拉框越乱IntelliSense 解析的时候要扫描的目录也越多首次打开解决方案的等待时间会明显变长。所以精确勾选不只是省磁盘也是省时间。2.1 工作负载勾选策略按项目类型反推我的习惯是先列清楚项目需要什么再倒推勾选。判断方法很简单打开现有解决方案的.vcxproj或.csproj看它引用了哪些 SDK 和工具集。纯 C/C 桌面程序或库只需要使用 C 的桌面开发这一个工作负载。右侧细节里会自动带上 MSVC v142 生成工具和 Windows 10 SDK这两样是关键。.NET Framework 或 .NET Core 应用勾.NET 桌面开发里面包含 WPF、WinForms 和 .NET Framework 4.8 的目标包。如果是 Web API 或 ASP.NET Core再加ASP.NET 和 Web 开发。需要 MFC/ATL 的老项目必须在单个组件里额外勾C MFC 最新版 v142和C ATL 最新版 v142。这两个不在默认推荐里漏勾的典型症状是打开项目时提示找不到 afxwin.h。要编译 Python 的 C 扩展或跑 node-gyp勾使用 C 的桌面开发就够了但必须确认 MSBuild 和 Windows SDK 都被勾上这是后面那类找不到 Visual Studio 实例报错的根源。Python 开发本身可以勾Python 开发工作负载但说实话我更倾向于只装 Python 官方发行版 VS Code让 VS2019 专注做它擅长的编译和调试。注意勾选工作负载后右侧的安装详细信息面板会出现包括推荐的组件和可选组件两栏。很多人只看左边的大类忽略了右边结果装完发现缺东西。养成习惯装之前把右侧列表从上到下扫一遍。2.2 单个组件粒度微调与磁盘占用控制工作负载之上还可以通过单个组件标签页做精细加减。这里我说几个实际会用到但容易被忽视的组件。C CMake 工具组件 ID 里带Microsoft.VisualStudio.Component.VC.CMake.Project——如果你用 CMake 管理工程这个东西一定要勾。它不只是让 VS 能打开CMakeLists.txt更重要的是提供了一套 CMake 与 MSBuild 之间的桥接逻辑。缺了它VS 只会把 CMake 文件当普通文本调试配置也不会自动生成。适用于 Windows 的 C Clang 工具——这个看情况。想用 Clang 做静态检查或者交叉编译到其他平台的勾上纯粹 MSVC 技术栈的可以不勾能省两三个 G。Git for Windows 和 NuGet 包管理器——默认自带别取消。特别是 NuGetC# 项目九十以上的第三方依赖都靠它。关于磁盘位置安装器允许分离三个路径IDE 安装目录、下载缓存目录、共享组件目录。我的经验是# 启动安装引导程序时指定路径避免全塞进 C 盘 vs_community.exe ^ --installPath D:\VS2019\Community ^ --sharedInstallPath D:\VS2019\Shared ^ --cache D:\VS2019\Cache加--cache false还能直接禁用本地缓存但我不建议——离线修复、重装、加组件的时候没有缓存就得重新下载。缓存在 SSD 上留 5 到 10GB 是划算的。2.3 离线布局制作与内网部署企业内网、无外网权限的构建机装 VS 唯一可行的路子是离线布局。做法是用一台能上网的机器下载完整包再拷贝进去。命令行大概是这样vs_community.exe --layout D:\vslayout ^ --lang zh-CN en-US ^ --add Microsoft.VisualStudio.Workload.NativeDesktop ^ --add Microsoft.VisualStudio.Workload.ManagedDesktop ^ --includeRecommended--add后面跟工作负载或组件 ID--includeRecommended表示把推荐组件一起拉下来。不写这一条装出来的环境会缺一堆东西然后在项目加载时才暴露出来。语言包按需加--lang zh-CN en-US两个都下中文界面加英语报错信息翻译这个组合用起来最舒服。下载完之后目标机器上执行D:\vslayout\vs_community.exe --noweb --add Microsoft.VisualStudio.Workload.NativeDesktop--noweb是强制离线一旦有缺失会明确报错而不是偷偷去联网。整包体积通常在 15GB 到 40GB 之间用移动硬盘拷之前记得确认文件系统——FAT32 单文件上限 4GB某些 SDK 的 cab 包会超务必用 NTFS 或 exFAT。提示离线布局下载过程中如果网络中断重新执行同样的命令会断点续传不会从头开始。这个特性救过我好几次。3. 把 VS2019 的编码彻底改成 UTF-8别只改一半编码这件事是中文开发者踩坑最多的地方没有之一。典型症状我都遇到过源码里中文注释在别人机器上变成一堆问号字符串常量里的中文编译出来是乱码编译时报C4819警告说文件包含当前代码页无法表示的字符更诡异的直接报C2001: 常量中有换行符明明那行代码好端端地待着。问题的根源在于MSVC 编译器判断源文件编码的逻辑是先看有没有 BOM有 BOM 就按 BOM 指定的编码解析没有 BOM 就按系统当前的 ANSI 代码页解析。简体中文 Windows 的默认代码页是 936GBK所以一个 UTF-8 无 BOM 的源文件编译器会当 GBK 读一个汉字三个字节被强行拆成 GBK 的双字节序列读出来的自然是乱码还可能把某个字节误判成引号或换行符。3.1 编辑器设置和文件保存编码两步都要做很多人只知道改一处实际上要分两层一是让编辑器正确识别和保存文件二是让编译器按 UTF-8 处理源文件。编辑器层面打开工具 → 选项 → 文本编辑器 → 常规把自动检测不带签名的 UTF-8 编码勾上。这个选项的意思是读取文件时如果内容看起来像合法的 UTF-8就按 UTF-8 处理而不是死按 GBK。它对读取有奇效但对保存没有约束力。保存层面VS 默认的保存编码是简体中文 (GB2312) - 代码页 936这个必须改。改的地方藏得比较深——文件 → 高级保存选项。如果菜单里没有这一项需要在工具 → 自定义 → 命令里从文件类别中把它拖出来。在对话框里把编码改成UTF-8 带签名 (BOM)然后保存。对已经有几百个源文件的老项目一个个点不现实。有两个更高效的办法。一是用.editorconfig统一约定。在解决方案根目录放一个文件内容root true [*] charset utf-8-bom end_of_line crlf insert_final_newline true indent_style space indent_size 4VS2019 原生支持读取.editorconfig16.x 早期版本需要装 EditorConfig Language Service 扩展后期已经内置。新创建的文件会遵循这个约定。注意utf-8-bom和utf-8的区别——前者带 BOM后者不带。二是批量转换已有文件。用一个 PowerShell 脚本扫一遍把无 BOM 的 UTF-8 和 GBK 文件统一成 UTF-8 with BOM# 把指定目录下的 .cpp/.h 文件统一转成 UTF-8 with BOM $utf8Bom New-Object System.Text.UTF8Encoding($true) Get-ChildItem -Path D:\Project\src -Recurse -Include *.cpp,*.h,*.hpp | ForEach-Object { $content [System.IO.File]::ReadAllText($_.FullName, [System.Text.Encoding]::Default) [System.IO.File]::WriteAllText($_.FullName, $content, $utf8Bom) }这段脚本的前提是这些文件当前确实是 GBK 编码用Encoding::Default读。执行前务必备份否则一旦猜错编码原文会被破坏得无法恢复。3.2 编译期加 /utf-8 才是治本手段就算文件都带了 BOM我仍然建议在项目级别显式指定编码因为你要防的是以后有人用别的编辑器创建了无 BOM 文件或者 CI 上用命令行工具生成了源文件。做法是在项目属性里C/C → 命令行 → 其他选项填入/utf-8这一个参数等价于同时指定/source-charset:utf-8 /execution-charset:utf-8。它告诉编译器源文件按 UTF-8 读字符串常量按 UTF-8 写进目标文件。如果你想更精细控制也可以分别写/source-charset:utf-8 /execution-charset:utf-8第一次看到执行字符集这个概念容易懵。类比一下源字符集是你怎么把字念出来执行字符集是你写进最终产物里的是什么形态。两者都设成 UTF-8才能保证从源码到二进制全程一致。如果是 CMake 工程加在CMakeLists.txt里if (MSVC) add_compile_options(/utf-8) endif()如果代码里用了L中文这种宽字符串还需要注意/execution-charset对宽字符的影响。宽字符走的是 UTF-16只要源字符集对了宽字符串基本不会出错这一点比窄字符串省心。3.3 几个容易漏掉的编码细节控制台输出乱码。程序里printf的中文在控制台显示成问号往往不是编译问题而是控制台代码页。运行前执行chcp 65001切到 UTF-8 代码页或者在代码里调用SetConsoleOutputCP(CP_UTF8)。不过要注意切了 65001 之后某些老的控制台字体渲染会有问题实际交付给客户时我更倾向于在程序启动时判断并设置而不是依赖外部环境。JSON、XML 配置文件的 BOM。带 BOM 的 JSON 在某些解析库尤其是用 C 写的轻量库比如 cJSON、RapidJSON 的部分用法里会解析失败因为 BOM 被当成非法字符。所以源文件用 UTF-8 with BOM但配置文件不要加 BOM。这是个反直觉的约定我曾经因为统一脚本把 JSON 也加了 BOM导致生产环境一个配置读取模块挂了两小时。Git 的换行和编码。.gitattributes里加上*.cs text eolcrlf之类的规则会更稳妥。真正的编码问题 Git 管不了它只存字节。但换行符如果混了 LF 和 CRLF某些对行尾敏感的代码比如 C# 的逐行字符串会表现不一致。数据库和日志文件的编码。程序内部是 UTF-8但写进 MySQL 的时候如果连接串没指定characterEncodingutf8驱动可能按系统代码页转一次中文就成乱码了。这是跨系统协作时最常见的我本地是好的排查的时候永远先看连接层。