推测故障控制面板扩展如何截断了它面前的值

The Old New Thing (Raymond Chen) 新闻

摘要

Raymond Chen 推测故障控制面板扩展如何因将64位指针截断为32位而导致崩溃,可能是由于64位移植过程中代码更新不完整。

<p>上一次,<a title="关闭显示控制面板时无效函数指针的案例" href="https://devblogs.microsoft.com/oldnewthing/20260715-00/?p=112535">我们发现控制面板扩展中的崩溃是由指针截断引起的</a>。代码手里明明有一个完好的64位指针,但不知为何失去理智,选择了丢弃高32位。</p> <p>这种情况是怎么发生的?</p> <p>我猜测这段代码最初是完好的32位代码:</p> <pre>HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); SetWindowLong(hwndButton, GWL_WNDPROC, (LONG)g_originalWndProc); </pre> <p>然后他们将其重新编译为64位代码,并遇到了一个错误。</p> <pre>error C2065: 'GWL_WNDPROC': undeclared identifier </pre> <p>然后他们查阅文档,发现对于64位Windows,<a title="系统窗口和类额外字节的演变" href="https://devblogs.microsoft.com/oldnewthing/20260629-00/?p=112484"> <code>GWL_<wbr />WNDPROC</code> 被重命名为 <code>GWLP_<wbr />WNDPROC</code></a>。</p> <p>于是他们通过将 <code>GWL_<wbr />WNDPROC</code> 改为 <code>GWLP_<wbr />WNDPROC</code> 来修复。</p> <pre>HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); SetWindowLong(hwndButton, <span style="border: solid 1px currentcolor;">GWL_WNDPROC</span>, (LONG)g_originalWndProc); </pre> <p>然而,重命名该值的目的并不是为了烦你。重命名该值是为了提醒你注意可能发生指针截断的地方。在这个例子中,是最后一个参数,即原始的64位窗口过程。构建中断告诉你,你可能将一个32位值传递给了应该是64位的位置。在这个例子中,因为它被强制转换为 <code>(LONG)</code>。你应该将 <code>GWL_<wbr />WNDPROC</code> 升级为 <code>GWLP_<wbr />WNDPROC</code>,同时将强制转换从 <code>(LONG)</code> 升级为 <code>(LONG_PTR)</code>。</p> <pre>HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); SetWindowLong(hwndButton, <span style="border: solid 1px currentcolor;">GWL_WNDPROC</span>, (<span style="border: solid 1px currentcolor;">LONG_PTR</span>)g_originalWndProc); </pre> <p>现在,这很可能是一个疏忽,而不是系统性错误,因为他们确实成功地正确子类化了窗口:</p> <pre>WNDPROC g_originalWndProc; HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); g_originalWndProc = (WNDPROC)SetWindowLong(hwndButton, <span style="border: solid 1px currentcolor;">GWLP_WNDPROC</span>, (<span style="border: solid 1px currentcolor;">LONG_PTR</span>)subclassWndProc); </pre> <p>他们只是遗漏了一处。也许是开发者在修复符号名称后分心了,忘记回来修复指针。</p> <p>下次,我们将探讨为什么这个bug这么长时间都没有被修复。</p> <p>本文 <a href="https://devblogs.microsoft.com/oldnewthing/20260716-00/?p=112539">《推测故障控制面板扩展如何截断了它面前的值》</a> 最初发表于 <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>。</p>
查看原文
查看缓存全文

缓存时间: 2026/07/16 22:54

# 推测有问题的控制面板扩展如何截断一个它本来就在手头的值 - 《老调重弹》 来源:https://devblogs.microsoft.com/oldnewthing/20260716-00?p=112539 上一次,我们发现了控制面板扩展崩溃的原因是指针截断(https://devblogs.microsoft.com/oldnewthing/20260715-00/?p=112535)。代码中明明有一个完好的 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`(https://devblogs.microsoft.com/oldnewthing/20260629-00/?p=112484)。 所以他们将 `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 Chen Raymond 已经参与 Windows 的演进超过 30 年。2003 年,他创办了名为《老调重弹》的网站,其受欢迎程度远超他最大胆的想象——这一发展至今仍让他感到毛骨悚然。该网站还衍生出了一本同名书籍(Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账号上,讲述一些毫无实际信息的故事。

相似文章

关闭显示控制面板时无效函数指针的案例

The Old New Thing (Raymond Chen)

这篇来自 The Old New Thing 的文章调查了 Windows 显示控制面板中一个常见的崩溃问题,该问题是由一个被截断为 32 位并进行符号扩展的无效函数指针引起的。作者通过分析崩溃转储来找出根本原因。

关于滥用Windows窗口类额外字节的兼容性说明

The Old New Thing (Raymond Chen)

Raymond Chen 讨论了一个历史性的 Windows 兼容性问题,其中一些 16 位程序滥用窗口类额外字节来存储私有数据,以及微软如何在保持向后兼容性的同时,对 32 位和 64 位程序堵住了这个漏洞。

Windows堆栈限制检查回顾,后续

The Old New Thing (Raymond Chen)

Raymond Chen跟进了他之前关于ARM64堆栈限制检查的文章,指出了堆栈探测函数中x15寄存器的非常规使用细节,并比较了多个架构的寄存器使用。