返回 2026-07-17
⚙️ 工程

推测有缺陷的控制面板扩展是如何截断它面前的值的Speculating on how the buggy control panel extension truncated a value that it had right in front of it

Raymond Chen 通过逆向推理,分析了一个有缺陷的 Windows 控制面板扩展为何会截断它本应正确读取的值。文章通过推断代码的历史演变过程,推测出可能是由于早期 API 限制或遗留逻辑导致的数据处理错误。这展示了通过现有现象反推代码历史背景的经典调试思路。

Raymond Chen

上次,我们发现控制面板扩展程序的崩溃是由指针截断引起的。代码原本持有一个完好的 64 位指针,但不知怎么却失去了理智,选择丢弃了高 32 位。

怎么会发生这样的事情呢?

我的猜测是,这段代码最初是完好的 32 位代码:

HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON);
SetWindowLong(hwndButton, GWL_WNDPROC, (LONG)g_originalWndProc);

然后他们将其重新编译为 64 位代码并遇到了错误。

error C2065: 'GWL_WNDPROC': undeclared identifier

接着他们回去查阅文档,发现对于 64 位 Windows,GWL_WNDPROC 已被重命名为 GWLP_WNDPROC。

所以他们通过将 GWL_WNDPROC 改为 GWLP_WNDPROC 来修复它。

HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON);
SetWindowLong(hwndButton, GWL_WNDPROC, (LONG)g_originalWndProc);

然而,重命名该值的目的并不是为了惹恼你。重命名该值的目的是引起你对可能发生指针截断的地方的注意。在这个例子中,就是最后一个参数,即原始的 64 位窗口过程。构建中断在告诉你,你可能将一个 32 位值传递给了本应是 64 位的东西。在这个例子中,是因为它被强制转换为了 (LONG)。你需要将 GWL_WNDPROC 升级为 GWLP_WNDPROC,同时将强制转换从 (LONG) 升级为 (LONG_PTR)。

HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON);
SetWindowLong(hwndButton, GWL_WNDPROC, (LONG_PTR)g_originalWndProc);

现在看来,这可能是一个疏忽,而不是系统性的失败,因为他们确实设法正确地对窗口进行了子类化:

WNDPROC g_originalWndProc;

HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON);
g_originalWndProc = (WNDPROC)SetWindowLong(hwndButton, GWLP_WNDPROC,
    (LONG_PTR)subclassWndProc);

他们只是漏掉了一个地方。也许开发人员在修复了符号名称后分心了,忘记回来修复指针。

下次,我们将看看为什么这个 bug 这么长时间都没有被修复。

作者

Raymond 参与了 Windows 的演进超过 30 年。2003 年,他创办了一个名为 The Old New Thing 的网站,其受欢迎程度远远超出了他最疯狂的想象,这一发展至今仍让他感到心惊肉跳。这个网站衍生出了一本书,巧合的是书名也叫 The Old New Thing(Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账户上,讲一些毫无用处信息的故事。

需要完整排版与评论请前往来源站点阅读。