The article traces the evolution of window and class extra bytes in Windows from 16-bit to 32-bit to 64-bit, detailing changes in function names and data sizes as handles and pointers expanded.
<p>Windows provides a family of functions for accessing so-called “extra bytes”. There are two categories of extra bytes: Class extra bytes (which belong to the window class) and window extra bytes (which belong to each window created from that class). Applications can request extra bytes at class registration, and those are accessed at increasing offsets starting at zero. The system also defines a number of extra bytes, and those use negative offsets.</p>
<p>We’re going to look at the system-defined offsets.</p>
<p>In 16-bit Windows, these were the available extra bytes and the function you used to read them:</p>
<table style="border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3">
<tbody>
<tr>
<th>Name</th>
<th>Size</th>
<th>Accessor</th>
<th>Notes</th>
</tr>
<tr>
<td><tt>GCW_MENUNAME</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCW_HBRBACKGROUND</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCW_HCURSOR</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCW_HICON</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCW_HMODULE</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCW_CBWNDEXTRA</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCW_CBCLSEXTRA</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCL_WNDPROC</tt></td>
<td>int32_t</td>
<td><tt>GetClassLong</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCW_STYLE</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GCW_ATOM</tt></td>
<td>int16_t</td>
<td><tt>GetClassWord</tt></td>
<td>Added in Windows 3.1</td>
</tr>
<tr>
<td><tt>GWL_WNDPROC</tt></td>
<td>int32_t</td>
<td><tt>GetWindowLong</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GWW_HINSTANCE</tt></td>
<td>int16_t</td>
<td><tt>GetWindowWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GWW_HWNDPARENT</tt></td>
<td>int16_t</td>
<td><tt>GetWindowWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GWW_ID</tt></td>
<td>int16_t</td>
<td><tt>GetWindowWord</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GWL_STYLE</tt></td>
<td>int32_t</td>
<td><tt>GetWindowLong</tt></td>
<td> </td>
</tr>
<tr>
<td><tt>GWL_EXSTYLE</tt></td>
<td>int32_t</td>
<td><tt>GetWindowLong</tt></td>
<td>Added in Windows 3.0</td>
</tr>
<tr>
<td><tt>DWL_MSGRESULT</tt></td>
<td>int32_t</td>
<td><tt>GetWindowLong</tt></td>
<td>For dialog windows</td>
</tr>
<tr>
<td><tt>DWL_DLGPROC</tt></td>
<td>int32_t</td>
<td><tt>GetWindowLong</tt></td>
<td>For dialog windows</td>
</tr>
<tr>
<td><tt>DWL_USER</tt></td>
<td>int32_t</td>
<td><tt>GetWindowLong</tt></td>
<td>For dialog windows</td>
</tr>
</tbody>
</table>
<p>There is clearly a naming pattern here for class and window bytes.</p>
<p>The first letter <tt>G</tt> stands for <tt>Get</tt>. The second letter <tt>C</tt> or <tt>W</tt> stands for <tt>Class</tt> or <tt>Window</tt>. And the third letter <tt>W</tt> or <tt>L</tt> stands for <tt>Word</tt> or <tt>Long</tt>.¹</p>
<p>For window bytes that apply only to dialog windows, the first letter changes to <tt>D</tt> for “dialog”. These values are zero or positive, since they are really just extra bytes registered to the standard dialog class.</p>
<p>Now, in 16-bit Windows, handles were 16-bit values, but in 32-bit Windows, they expand to 32-bit values, so 32-bit Windows changed the functions from <code>GetSomething<span style="border: solid 1px currentcolor;">Word</span></code> to <code>GetSomething<span style="border: solid 1px currentcolor;">Long</span></code>, and the prefixes correspondingly changed from <tt>W</tt> to from <tt>L</tt>. So our table now looks like this:</p>
<table style="border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3">
<tbody>
<tr>
<th>Name</th>
<th>16-bit prefix/size</th>
<th>32-bit prefix/size</th>
</tr>
<tr>
<td><tt>MENUNAME</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>HBRBACKGROUND</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>HCURSOR</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>HICON</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>HMODULE</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>CBWNDEXTRA</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>CBCLSEXTRA</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>WNDPROC</tt></td>
<td><tt>GCL_</tt> int32_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>STYLE</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>ATOM</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCW_</tt> int16_t</td>
</tr>
<tr>
<td><tt>HICONSM</tt></td>
<td> </td>
<td><tt>GCL_</tt> int32_t <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a5.png" alt="💥" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td>
</tr>
<tr>
<td><tt>WNDPROC</tt></td>
<td><tt>GWL_</tt> int32_t</td>
<td><tt>GWL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>HWNDPARENT</tt></td>
<td><tt>GWW_</tt> int16_t</td>
<td><tt>GWL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>ID</tt></td>
<td><tt>GWW_</tt> int16_t</td>
<td><tt>GWL_</tt> int32_t ◱</td>
</tr>
<tr>
<td><tt>STYLE</tt></td>
<td><tt>GWL_</tt> int32_t</td>
<td><tt>GWL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>EXSTYLE</tt></td>
<td><tt>GWL_</tt> int32_t</td>
<td><tt>GWL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>USERDATA</tt></td>
<td> </td>
<td><tt>GWL_</tt> int32_t <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a5.png" alt="💥" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td>
</tr>
<tr>
<td><tt>MSGRESULT</tt></td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>DLGPROC</tt></td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>USER</tt></td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWL_</tt> int32_t</td>
</tr>
</tbody>
</table>
<p>The ◱ symbol represents a value that got bigger, and the <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a5.png" alt="💥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> symbol represents values that did not exist in 16-bit Windows.</p>
<p>Even though control IDs are typically small integers, the space for them was expanded from a 16-bit value to a 32-bit value because <a title="Why are timer IDs and dialog control IDs 64-bit values on 64-bit Windows? Did you really expect people to create more than 4 billion timers or dialog controls?" href="https://devblogs.microsoft.com/oldnewthing/20191010-00/?p=102978"> some people were using it to hold pointers or handles</a>. (One way to create a process-wide unique number is to <a title="A window can't have two timers with the same ID, so how do I assign an ID that nobody else is using?" href="https://devblogs.microsoft.com/oldnewthing/20191009-00/?p=102974"> allocate memory and use its address</a>.)</p>
<p>The next step in the evolution of extra bytes is the conversion from 32-bit to 64-bit Windows. Pointers and handles expand to 64-bit values on 64-bit Windows, so all of the extra bytes that are used to (or could be used to) hold a handle or pointer were expanded to a 64-bit version.</p>
<p>To make it possible to write code that targets both 32-bit and 64-bit Windows, the design of 64-bit Windows didn’t make the hard break that 32-bit Windows did from 16-bit Windows. Instead, they introduced new functions that accept pointer-sized integers, which are 32-bit values on 32-bit Windows and 64-bit values on 64-bit Windows. That way, you just use those new functions everywhere, and they will expand on 64-bit systems and remain the same on 32-bit systems.</p>
<p>The new functions have names like <code>GetWindowLong<span style="border: solid 1px currentcolor;">Ptr</span></code>, and the corresponding prefixes were changed to <code>GWLP_</code> and so on.</p>
<table style="border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3">
<tbody>
<tr>
<th>Name</th>
<th>16-bit prefix/size</th>
<th>32-bit prefix/size</th>
<th>32/64-bit prefix/size</th>
</tr>
<tr>
<td><tt>MENUNAME</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>HBRBACKGROUND</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>HCURSOR</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>HICON</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>HMODULE</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>CBWNDEXTRA</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>CBCLSEXTRA</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>WNDPROC</tt></td>
<td><tt>GCL_</tt> int32_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>STYLE</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCL_</tt> int32_t ◱</td>
<td><tt>GCL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>ATOM</tt></td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCW_</tt> int16_t</td>
<td><tt>GCW_</tt> int16_t</td>
</tr>
<tr>
<td><tt>HICONSM</tt></td>
<td> </td>
<td><tt>GCL_</tt> int32_t <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a5.png" alt="💥" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td>
<td><tt>GCLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>WNDPROC</tt></td>
<td><tt>GWL_</tt> int32_t</td>
<td><tt>GWL_</tt> int32_t ◱</td>
<td><tt>GWLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>HWNDPARENT</tt></td>
<td><tt>GWW_</tt> int16_t</td>
<td><tt>GWL_</tt> int32_t ◱</td>
<td><tt>GWLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>ID</tt></td>
<td><tt>GWW_</tt> int16_t</td>
<td><tt>GWL_</tt> int32_t ◱</td>
<td><tt>GWLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>STYLE</tt></td>
<td><tt>GWL_</tt> int32_t</td>
<td><tt>GWL_</tt> int32_t</td>
<td><tt>GWL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>EXSTYLE</tt></td>
<td><tt>GWL_</tt> int32_t</td>
<td><tt>GWL_</tt> int32_t</td>
<td><tt>GWL_</tt> int32_t</td>
</tr>
<tr>
<td><tt>USERDATA</tt></td>
<td> </td>
<td><tt>GWL_</tt> int32_t <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a5.png" alt="💥" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td>
<td><tt>GWLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>MSGRESULT</tt></td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>DLGPROC</tt></td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWLP_</tt> intptr_t ◱</td>
</tr>
<tr>
<td><tt>USER</tt></td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWL_</tt> int32_t</td>
<td><tt>DWLP_</tt> intptr_t ◱</td>
</tr>
</tbody>
</table>
<p>From the prefix on the name of the extra bytes, you can read off which function it is meant to be used with.</p>
<table style="border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3">
<tbody>
<tr>
<th>Prefix</th>
<th>Function</th>
</tr>
<tr>
<td><tt>GCW_</tt> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <tt><u>G</u>et<u>C</u>lass<u>W</u>ord</tt></td>
<td><tt>GWW_</tt> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <tt><u>G</u>et<u>W</u>indow<u>W</u>ord</tt></td>
</tr>
<tr>
<td><tt>GCL_</tt> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <tt><u>G</u>et<u>C</u>lass<u>L</u>ong</tt></td>
<td><tt>GWL_</tt> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <tt><u>G</u>et<u>W</u>indow<u>L</u>ong</tt></td>
</tr>
<tr>
<td><tt>GCLP_</tt> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <tt><u>G</u>et<u>C</u>lass<u>L</u>ong<u>P</u>tr</tt></td>
<td><tt>GWLP_</tt> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <tt><u>G</u>et<u>W</u>indow<u>L</u>ong<u>P</u>tr</tt></td>
</tr>
</tbody>
</table>
<p>The weirdo is <tt>DWLP_</tt> because it needs to encode both the type of window that it can be used with (D = dialog) as well as the function name it goes with (<code><u>W</u>indow<u>L</u>ong<u>P</u>tr</code>).</p>
<p>As a concession, Windows lets you pass <tt>GCL_</tt> and <tt>GWL_</tt> values to <code>GetClassLongPtr</code> and <code>GetWindowLongPtr</code> (respectively) even though they are intended to be used with <code>GetClassLong</code> and <code>GetWindowLong</code> (respectively). If you do that, you get the corresponding 32-bit value zero-extended if necessary to be the size of a pointer.² This is seen primarily in the case of <tt>GWL_ID</tt> because most people don’t use the full range of IDs, so if you’re willing to live within the 32-bit subset, you can just pretend that the values are not pointer-sized.³</p>
<p>“Why bother changing all the prefixes? Doesn’t that just create a lot of busy work for people porting from 32-bit code to 64-bit code?”</p>
<p>Yes, but it’s good busy work. The point is to force build breaks at places where you need to make fixes, because you have to call the function that accesses a pointer-sized integer rather than a 32-bit integer; otherwise you suffer from integer truncation bugs.</p>
<p>¹ This is a common prefixing convention for classic Win32. For example, the operation parameter to <code>ShowWindow</code> is prefixed <code>SW_</code>; the flags to <code>SetWindowPos</code> are prefixed <code>SWP_</code>; and the relationship parameter for <code>GetWindow</code> is prefixed <code>GW_</code>.</p>
<p>² The use of the <tt>GWL_</tt> values with <code>SetWindowLongPtr</code> is a bit more problematic. It looks like you’re storing a pointer-sized integer, but only the bottom 32 bits are honored.</p>
<p>³ The <tt>ID</tt> is unusual in that it is defined both as <tt>GWL_ID</tt> and <tt>GWLP_ID</tt>. All of the other values are defined with only one prefix.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260629-00/?p=112484">The evolution of window and class extra bytes in Windows</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
# The evolution of window and class extra bytes in Windows - The Old New Thing
Source: [https://devblogs.microsoft.com/oldnewthing/20260629-00?p=112484](https://devblogs.microsoft.com/oldnewthing/20260629-00?p=112484)
Windows provides a family of functions for accessing so\-called “extra bytes”\. There are two categories of extra bytes: Class extra bytes \(which belong to the window class\) and window extra bytes \(which belong to each window created from that class\)\. Applications can request extra bytes at class registration, and those are accessed at increasing offsets starting at zero\. The system also defines a number of extra bytes, and those use negative offsets\.
We’re going to look at the system\-defined offsets\.
In 16\-bit Windows, these were the available extra bytes and the function you used to read them:
NameSizeAccessorNotesGCW\_MENUNAMEint16\_tGetClassWordGCW\_HBRBACKGROUNDint16\_tGetClassWordGCW\_HCURSORint16\_tGetClassWordGCW\_HICONint16\_tGetClassWordGCW\_HMODULEint16\_tGetClassWordGCW\_CBWNDEXTRAint16\_tGetClassWordGCW\_CBCLSEXTRAint16\_tGetClassWordGCL\_WNDPROCint32\_tGetClassLongGCW\_STYLEint16\_tGetClassWordGCW\_ATOMint16\_tGetClassWordAdded in Windows 3\.1GWL\_WNDPROCint32\_tGetWindowLongGWW\_HINSTANCEint16\_tGetWindowWordGWW\_HWNDPARENTint16\_tGetWindowWordGWW\_IDint16\_tGetWindowWordGWL\_STYLEint32\_tGetWindowLongGWL\_EXSTYLEint32\_tGetWindowLongAdded in Windows 3\.0DWL\_MSGRESULTint32\_tGetWindowLongFor dialog windowsDWL\_DLGPROCint32\_tGetWindowLongFor dialog windowsDWL\_USERint32\_tGetWindowLongFor dialog windowsThere is clearly a naming pattern here for class and window bytes\.
The first letterGstands forGet\. The second letterCorWstands forClassorWindow\. And the third letterWorLstands forWordorLong\.¹
For window bytes that apply only to dialog windows, the first letter changes toDfor “dialog”\. These values are zero or positive, since they are really just extra bytes registered to the standard dialog class\.
Now, in 16\-bit Windows, handles were 16\-bit values, but in 32\-bit Windows, they expand to 32\-bit values, so 32\-bit Windows changed the functions from`GetSomethingWord`to`GetSomethingLong`, and the prefixes correspondingly changed fromWto fromL\. So our table now looks like this:
Name16\-bit prefix/size32\-bit prefix/sizeMENUNAMEGCW\_int16\_tGCL\_int32\_t ◱HBRBACKGROUNDGCW\_int16\_tGCL\_int32\_t ◱HCURSORGCW\_int16\_tGCL\_int32\_t ◱HICONGCW\_int16\_tGCL\_int32\_t ◱HMODULEGCW\_int16\_tGCL\_int32\_t ◱CBWNDEXTRAGCW\_int16\_tGCL\_int32\_t ◱CBCLSEXTRAGCW\_int16\_tGCL\_int32\_t ◱WNDPROCGCL\_int32\_tGCL\_int32\_t ◱STYLEGCW\_int16\_tGCL\_int32\_t ◱ATOMGCW\_int16\_tGCW\_int16\_tHICONSMGCL\_int32\_t 💥WNDPROCGWL\_int32\_tGWL\_int32\_t ◱HWNDPARENTGWW\_int16\_tGWL\_int32\_t ◱IDGWW\_int16\_tGWL\_int32\_t ◱STYLEGWL\_int32\_tGWL\_int32\_tEXSTYLEGWL\_int32\_tGWL\_int32\_tUSERDATAGWL\_int32\_t 💥MSGRESULTDWL\_int32\_tDWL\_int32\_tDLGPROCDWL\_int32\_tDWL\_int32\_tUSERDWL\_int32\_tDWL\_int32\_tThe ◱ symbol represents a value that got bigger, and the 💥 symbol represents values that did not exist in 16\-bit Windows\.
Even though control IDs are typically small integers, the space for them was expanded from a 16\-bit value to a 32\-bit value because[some people were using it to hold pointers or handles](https://devblogs.microsoft.com/oldnewthing/20191010-00/?p=102978)\. \(One way to create a process\-wide unique number is to[allocate memory and use its address](https://devblogs.microsoft.com/oldnewthing/20191009-00/?p=102974)\.\)
The next step in the evolution of extra bytes is the conversion from 32\-bit to 64\-bit Windows\. Pointers and handles expand to 64\-bit values on 64\-bit Windows, so all of the extra bytes that are used to \(or could be used to\) hold a handle or pointer were expanded to a 64\-bit version\.
To make it possible to write code that targets both 32\-bit and 64\-bit Windows, the design of 64\-bit Windows didn’t make the hard break that 32\-bit Windows did from 16\-bit Windows\. Instead, they introduced new functions that accept pointer\-sized integers, which are 32\-bit values on 32\-bit Windows and 64\-bit values on 64\-bit Windows\. That way, you just use those new functions everywhere, and they will expand on 64\-bit systems and remain the same on 32\-bit systems\.
The new functions have names like`GetWindowLongPtr`, and the corresponding prefixes were changed to`GWLP\_`and so on\.
Name16\-bit prefix/size32\-bit prefix/size32/64\-bit prefix/sizeMENUNAMEGCW\_int16\_tGCL\_int32\_t ◱GCLP\_intptr\_t ◱HBRBACKGROUNDGCW\_int16\_tGCL\_int32\_t ◱GCLP\_intptr\_t ◱HCURSORGCW\_int16\_tGCL\_int32\_t ◱GCLP\_intptr\_t ◱HICONGCW\_int16\_tGCL\_int32\_t ◱GCLP\_intptr\_t ◱HMODULEGCW\_int16\_tGCL\_int32\_t ◱GCLP\_intptr\_t ◱CBWNDEXTRAGCW\_int16\_tGCL\_int32\_t ◱GCL\_int32\_tCBCLSEXTRAGCW\_int16\_tGCL\_int32\_t ◱GCL\_int32\_tWNDPROCGCL\_int32\_tGCL\_int32\_t ◱GCLP\_intptr\_t ◱STYLEGCW\_int16\_tGCL\_int32\_t ◱GCL\_int32\_tATOMGCW\_int16\_tGCW\_int16\_tGCW\_int16\_tHICONSMGCL\_int32\_t 💥GCLP\_intptr\_t ◱WNDPROCGWL\_int32\_tGWL\_int32\_t ◱GWLP\_intptr\_t ◱HWNDPARENTGWW\_int16\_tGWL\_int32\_t ◱GWLP\_intptr\_t ◱IDGWW\_int16\_tGWL\_int32\_t ◱GWLP\_intptr\_t ◱STYLEGWL\_int32\_tGWL\_int32\_tGWL\_int32\_tEXSTYLEGWL\_int32\_tGWL\_int32\_tGWL\_int32\_tUSERDATAGWL\_int32\_t 💥GWLP\_intptr\_t ◱MSGRESULTDWL\_int32\_tDWL\_int32\_tDWLP\_intptr\_t ◱DLGPROCDWL\_int32\_tDWL\_int32\_tDWLP\_intptr\_t ◱USERDWL\_int32\_tDWL\_int32\_tDWLP\_intptr\_t ◱From the prefix on the name of the extra bytes, you can read off which function it is meant to be used with\.
PrefixFunctionGCW\_↔GetClassWordGWW\_↔GetWindowWordGCL\_↔GetClassLongGWL\_↔GetWindowLongGCLP\_↔GetClassLongPtrGWLP\_↔GetWindowLongPtrThe weirdo isDWLP\_because it needs to encode both the type of window that it can be used with \(D = dialog\) as well as the function name it goes with \(`WindowLongPtr`\)\.
As a concession, Windows lets you passGCL\_andGWL\_values to`GetClassLongPtr`and`GetWindowLongPtr`\(respectively\) even though they are intended to be used with`GetClassLong`and`GetWindowLong`\(respectively\)\. If you do that, you get the corresponding 32\-bit value zero\-extended if necessary to be the size of a pointer\.² This is seen primarily in the case ofGWL\_IDbecause most people don’t use the full range of IDs, so if you’re willing to live within the 32\-bit subset, you can just pretend that the values are not pointer\-sized\.³
“Why bother changing all the prefixes? Doesn’t that just create a lot of busy work for people porting from 32\-bit code to 64\-bit code?”
Yes, but it’s good busy work\. The point is to force build breaks at places where you need to make fixes, because you have to call the function that accesses a pointer\-sized integer rather than a 32\-bit integer; otherwise you suffer from integer truncation bugs\.
¹ This is a common prefixing convention for classic Win32\. For example, the operation parameter to`ShowWindow`is prefixed`SW\_`; the flags to`SetWindowPos`are prefixed`SWP\_`; and the relationship parameter for`GetWindow`is prefixed`GW\_`\.
² The use of theGWL\_values with`SetWindowLongPtr`is a bit more problematic\. It looks like you’re storing a pointer\-sized integer, but only the bottom 32 bits are honored\.
³ TheIDis unusual in that it is defined both asGWL\_IDandGWLP\_ID\. All of the other values are defined with only one prefix\.
### 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\.
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.
This video uses disassembly and the WinDbg debugging tool to reveal that the eight mystery bytes that appear when calloc allocates memory in an x86 environment are actually the entry header of the Windows heap manager, containing information such as the current block size and the previous block size.
This article explains how the WM_COPYDATA message, introduced for 32-bit Windows, was retrofitted onto 16-bit Windows 3.1 by taking advantage of the shared address space, requiring no changes for inter-16-bit communication.
Explains the historical reason why 32-bit Windows client editions artificially limit RAM to 4 GB: driver compatibility issues with Physical Address Extensions (PAE) and Data Execution Prevention (DEP), as opposed to any nefarious motive.
A historical look at how the Windows operating system UI for opening unassociated files evolved from Windows 386 to Windows XP, showing the progression of dialog boxes and the introduction of web service prompts.