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’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’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>
# 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 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\.
This article from The Old New Thing investigates a common crash in the Windows display control panel caused by an invalid function pointer that was truncated to 32-bit and sign-extended. The author analyzes a crash dump to identify the root cause.
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.
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.
The article explains why a bug in the display control panel related to pointer truncation persists despite being fixed by the vendor, due to computer manufacturers' driver certification processes and users being stuck with outdated certified drivers.
A technical blog post by Raymond Chen investigating a memory corruption bug where a single byte 0x01 corrupts HMODULE handles, causing DLLs to be improperly freed and leading to crashes at process termination.