我曾在 The Old New Thing 上露过脸
我是个相当低调的人,所以大多数人都不知道关于我的这个极其了不起的事实:Raymond Chen(雷蒙德·陈)曾经提到过我,就在经典 Windows 开发博客 The Old New Thing(《旧闻新知》)上。
不,他没有提到我的名字,也没有提供任何能识别出我的方式,但我依然值得称赞——因为我如此克制,几乎从不炫耀这一惊人成就。

2009 年,Raymond Chen 在一期 The Old New Thing》中提到了我。
我想解决的问题
Raymond 在那篇帖子中把我描述为“一位客户”,但其实当时我是他在微软的同事。那年我 23 岁,在微软做开发快两年了,那是我大学毕业后的第一份工作。
我当时负责 BitLocker——Windows 中加密磁盘驱动器的功能。我们正开始开发 Windows 8,我的项目是改进 BitLocker 的配置体验。
BitLocker 有许多旋钮和开关,管理员可以通过组织级设置(用 Windows 的术语说就是 Group Policy(组策略))来配置。IT 管理员可以在整个组织中强制执行一条规则,比如“每个人的 BitLocker 密码短语必须至少 12 个字符长”,然后 BitLocker 就会强制用户创建至少 12 个字符的密码短语。

通过 Windows 组策略编辑器查看的 BitLocker 配置选项
BitLocker 配置的一个令人头疼的问题是错误信息含糊不清。如果你试图配置一条要求密码短语至少 1000 个字符的规则,BitLocker 会抛出一个类似“不行,太长了”的错误,但不会告诉你上限是多少。
在微软,C++ 代码里不能包含错误信息,因为本地化团队需要把所有面向用户的文本翻译成其他语言。因此,所有面向用户的文本都存放在类似这样的 .mc 文件中:
SymbolicName=ERROR_BITLOCKER_PASSPHRASE_MINIMUM_TOO_LONG
The BitLocker minimum passphrase length is too high.
.
SymbolicName=...
然后在 C++ 代码的某处,我们会写一个类似这样的检查:
#define MAX_PASSPHRASE_MINIMUM 20
UINT32 minimumPassphraseLength = ReadGroupPolicy(GP_BITLOCKER_MINIMUM_PASSPHRASE_LENGTH);
if (minimumPassphraseLength > MAX_PASSPHRASE_MINIMUM) {
ShowError(ERROR_BITLOCKER_PASSPHRASE_MINIMUM_TOO_LONG);
}
我想修改 BitLocker 的错误信息,让它们向用户说明错误发生的具体原因。也就是说,用户看到的不再是:
The BitLocker minimum passphrase length is too high.
而是:
The BitLocker minimum passphrase length cannot exceed 20.
我不想把 C++ 代码中的值 20 复制到 .mc 文件里,因为如果以后修改了 MAX_PASSPHRASE_MINIMUM 的值,它就会与 .mc 文件不同步,导致错误信息出错。
Raymond Chen 是怎么介入的
我对处理 .mc 文件的 Message Compiler 工具了解不多。我找不到任何人在 .mc 文件中引用 C++ 值的例子,但我觉得应该有某种办法做到这一点。
我在公司内部邮件列表上问,能不能这样写 .mc 文件:
SymbolicName=ERROR_BITLOCKER_PASSPHRASE_MINIMUM_TOO_LONG
The BitLocker minimum passphrase length cannot exceed ${MAX_PASSPHRASE_MINIMUM}.
Raymond Chen 经常在这些邮件列表上发帖。即使在 2009 年,他也已经在微软待了很久很久,对与 Windows 开发相关的一切都有着百科全书式的知识。他的回复既 helpful 又权威,但如果他觉得你提问前没有做足功课,就会带点毒舌。
如果我没记错的话,Raymond 对我的帖子回了一条简短的回复:“没有什么法律禁止你使用预处理器”,并附上一个用预处理器命令生成 .mc 文件的示例。
我花了好一阵子才明白他想告诉我什么。我根本不知道你居然可以让 C++ 编译器只运行预处理步骤。
浪费了 Raymond Chen 的时间
这个故事最丢人的部分是:尽管我从伟大的 Raymond Chen 那里得到了建议,我却临阵退缩,没用上。
在 Raymond Chen 的博客文章中,他展示了只需改动 Makefile 里的几行代码,就能让你的源文件从 .mc 文件变成 .mcp 文件。轻而易举!
而 Windows 构建系统比 Makefile 复杂了无数倍。我已经记不清它长什么样了,只记得它让我感到恐惧和困惑。
更糟的是,如果你搞砸了构建,可能要到第二天早上收到一封邮件时才会发现你弄坏了 nightly build,而由于你的缘故,几十甚至几百个人当天都没有可用的 Windows 构建。
所以我面临一个选择。我可以成为第一个在构建系统里尝试新做法的人,冒着花一两周时间修复意外问题的风险;或者我可以假装自己从没想过要把具体数字放进 BitLocker 的错误信息里,转而去寻找其他让配置更简单的方法。
我选择了后者。
即使到了今天我也不知道该怎么解决
当时我记得自己想:“哇,我居然不知道可以这样用 C 预处理器,真蠢。”
大多数时候,当我回顾多年前苦苦挣扎的软件问题时,如今会觉得解决方案显而易见。通常我能想出更好的办法。
但 16 年过去了,Raymond 那个对非 C/C++ 文件运行 C 预处理器的解决方案依然让我觉得出乎意料。如果我拥有全部的职业经验、唯独抹去这段关于 Raymond Chen 的记忆,然后你再让我重新解决这个问题,我还是会像 2009 年那样举步维艰。
不同的是,今天的我不会再为不知道怎么解决这个问题而觉得自己蠢了。我现在把它视为微软内部工具链的一个缺陷。在微软,在他们旗舰级的产品上,怎么会没有一个标准方法让开发者能同时在错误信息和 C++ 代码中引用常量值呢?
作为软件工程师,有些问题虽然让你不爽,但你可以咬紧牙关多加练习直到变强。另一些问题,你则可以通过谨慎挑选工作和项目来直接绕开。
理解晦涩难懂的构建系统就属于我一直回避的问题之一,而且我心安理得。除非我用 Nix 的时候。
随机一篇博客