I have the following code in a Win32 project written in C:
const HWND window = CreateWindowExW(...);
static_assert(alignof *window == alignof (int)); // 1. Always true
assert((uintptr_t) window % alignof (int) == 0); // 2. *FALSE* in general
assert((uintptr_t) window % alignof (short) == 0); // 3. Always true, at least in my testing
<windef.h>
includes the following code, which corroborates statement 1:
typedef struct HWND__ { // NOTE: This code is abridged for simplicity
int unused;
} *HWND;
Statements 1. and 2. are in inherent contradition. Statement 3. leads me to believe that this behavior is a holdover of the Win16 API, where sizeof (int) == 2
.
Why is this happening?
Is this a bug in the Win32 API?
HWND
might look like a pointer, but it is just a unique identifier with the same number of bits as a pointer that the Win32 Subsystem can then use for internal handle table lookups. Do not trust that typedef that's defined in the header; it is just defining a structure pointer to make sure that it's not implicitly used with other data types. (ie, if it was just uintptr_t
or PVOID
or an equivalent typedef, then it's difficult to differentiate a set of functions expecting the argument to be a window handle with another set of functions also using uintptr_t
for something completely different like a bitmap or icon handle).
Browsing through the source code for ReactOS (starting from the NtUserCreateWindowEx
function) indicates that the returned handle is just a handle table index shifted left by one bit (likely for legacy 16-bit compatibility, as you mentioned) and added to some pre-calculated constant base value. This doesn't necessarily mean that this is what is actually being used by the official Win32 sources, but it may give you an idea about how it may be working internally (given that ReactOS attempts to implement a binary compatible operating system for both user mode and kernel mode).
EDIT: As mentioned in the comments by user IInspectable, Raymond Chen has published two articles relating to this: How are window manager handles determined in 16-bit Windows and Windows 95? and the follow-up: How are window manager handles determined in Windows NT?