Speculating on how the buggy control panel extension truncated a value that it had right in front of it

The Old New Thing (Raymond Chen) News

Summary

Raymond Chen speculates on how a buggy control panel extension caused a crash by truncating a 64-bit pointer to 32 bits, likely due to incomplete code updates during 64-bit porting.

<p>Last time, <a title="The case of the invalid function pointer when shutting down the display control panel" href="https://devblogs.microsoft.com/oldnewthing/20260715-00/?p=112535"> we found that a crash in a control panel extension was caused by pointer truncation</a>. The code had a perfectly good 64-bit pointer in its hand, but somehow lost its mind and opted to throw away the top 32 bits.</p> <p>How could something like this happen?</p> <p>My guess is that this code started out as perfectly good 32-bit code:</p> <pre>HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); SetWindowLong(hwndButton, GWL_WNDPROC, (LONG)g_originalWndProc); </pre> <p>And then they recompiled it as 64-bit code and got an error.</p> <pre>error C2065: 'GWL_WNDPROC': undeclared identifier </pre> <p>They then went back to the documentation and saw that for 64-bit Windows, <a title="The evolution of system-windows window and class extra bytes" href="https://devblogs.microsoft.com/oldnewthing/20260629-00/?p=112484"> <code>GWL_<wbr />WNDPROC</code> was renamed to <code>GWLP_<wbr />WNDPROC</code></a>.</p> <p>So they fixed it by changing <code>GWL_<wbr />WNDPROC</code> to <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>However, the point of renaming the value was not to annoy you. The point of renaming the value was to call your attention to places where pointer truncation is likely to occur. In this case, it&#8217;s the final parameter, the original 64-bit window procedure. The build break is telling you that you are probably passing a 32-bit value as something that should be 64-bit. In this case, because it was being cast to <code>(LONG)</code>. You are expected to upgrade the <code>GWL_<wbr />WNDPROC</code> to <code>GWLP_<wbr />WNDPROC</code> and at the same time upgrade the cast from <code>(LONG)</code> to <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>Now, this was likely an oversight rather than a systemic failure, because they did manage to subclass the window properly:</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>They merely missed a spot. Perhaps the developer got distracted after fixing the symbol name and forgot to come back and fix the pointer.</p> <p>Next time, we&#8217;ll look at why this bug has remained unfixed for so long.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260716-00/?p=112539">Speculating on how the buggy control panel extension truncated a value that it had right in front of it</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
Original Article
View Cached Full Text

Cached at: 07/16/26, 10:54 PM

# Speculating on how the buggy control panel extension truncated a value that it had right in front of it - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260716-00?p=112539](https://devblogs.microsoft.com/oldnewthing/20260716-00?p=112539) Last time,[we found that a crash in a control panel extension was caused by pointer truncation](https://devblogs.microsoft.com/oldnewthing/20260715-00/?p=112535)\. The code had a perfectly good 64\-bit pointer in its hand, but somehow lost its mind and opted to throw away the top 32 bits\. How could something like this happen? My guess is that this code started out as perfectly good 32\-bit code: ``` HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); SetWindowLong(hwndButton, GWL_WNDPROC, (LONG)g_originalWndProc); ``` And then they recompiled it as 64\-bit code and got an error\. ``` error C2065: 'GWL_WNDPROC': undeclared identifier ``` They then went back to the documentation and saw that for 64\-bit Windows,[`GWL\_WNDPROC`was renamed to`GWLP\_WNDPROC`](https://devblogs.microsoft.com/oldnewthing/20260629-00/?p=112484)\. So they fixed it by changing`GWL\_WNDPROC`to`GWLP\_WNDPROC`\. ``` HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); SetWindowLong(hwndButton, GWL_WNDPROC, (LONG)g_originalWndProc); ``` However, the point of renaming the value was not to annoy you\. The point of renaming the value was to call your attention to places where pointer truncation is likely to occur\. In this case, it’s the final parameter, the original 64\-bit window procedure\. The build break is telling you that you are probably passing a 32\-bit value as something that should be 64\-bit\. In this case, because it was being cast to`\(LONG\)`\. You are expected to upgrade the`GWL\_WNDPROC`to`GWLP\_WNDPROC`and at the same time upgrade the cast from`\(LONG\)`to`\(LONG\_PTR\)`\. ``` HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); SetWindowLong(hwndButton, GWL_WNDPROC, (LONG_PTR)g_originalWndProc); ``` Now, this was likely an oversight rather than a systemic failure, because they did manage to subclass the window properly: ``` WNDPROC g_originalWndProc; HWND hwndButton = GetDlgItem(hdlg, ID_BUTTON); g_originalWndProc = (WNDPROC)SetWindowLong(hwndButton, GWLP_WNDPROC, (LONG_PTR)subclassWndProc); ``` They merely missed a spot\. Perhaps the developer got distracted after fixing the symbol name and forgot to come back and fix the pointer\. Next time, we’ll look at why this bug has remained unfixed for so long\. ### Category ### Topics ## Author ![Raymond Chen](https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2019/02/RaymondChen_5in-150x150.jpg) Raymond has been involved in the evolution of Windows for more than 30 years\. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie\-jeebies\. The Web site spawned a book, coincidentally also titled The Old New Thing \(Addison Wesley 2007\)\. He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information\.

Similar Articles

A compatibility note on the abuse of Windows window class extra bytes

The Old New Thing (Raymond Chen)

Raymond Chen discusses a historical Windows compatibility issue where some 16-bit programs abused window class extra bytes to store private data, and how Microsoft blocked the loophole for 32-bit and 64-bit programs while maintaining backward compatibility.

Windows stack limit checking retrospective, follow-up

The Old New Thing (Raymond Chen)

Raymond Chen follows up on his previous article about stack limit checking on ARM64, addressing a detail about the unconventional use of the x15 register in stack probe functions and comparing register usage across multiple architectures.