[Retro 考古] 解秘消失的 Windows Message 0x4

Those who have developed applications for Windows before should know something called a Windows Message. Windows communicates with your application window by passing messages to it. A message is simply a numeric code that designates a particular event. For example, if the user presses the left mouse button, the window receives a message that has the following message code:

#define WM_LBUTTONDOWN    0x0201

To pass a message to a window, the operating system calls the window procedure (WndProc) registered for that window. Windows Messages are defined in the header file WINUSER.H (in recent versions of Windows) or WINDOWS.H, and here is an excerpt from it:

#define WM_NULL                         0x0000
#define WM_CREATE                       0x0001
#define WM_DESTROY                      0x0002
#define WM_MOVE                         0x0003
#define WM_SIZE                         0x0005
#define WM_ACTIVATE                     0x0006
#define WM_SETFOCUS                     0x0007
#define WM_KILLFOCUS                    0x0008

Those who passed kindergarten math should quickly realize something – nothing defined as 0x4. Why? Because it was taken out of WINUSER.H/WINDOWS.H ages ago. Some people say once something gets added to WINDOWS.H, it will stay there forever, this is obviously an exception. Again, why? Because code for sending Windows Message 0x4 was already removed before the official release of Windows 1.0 (some point between Premiere Edition and RTM). It was never documented as a result of that, but it stayed in WINDOWS.H for a few more years (shows how much Microsoft cared about compatibility).

Now, let’s solve the mystery. We need to figure out the name of our mysterious Windows Message 0x4 first. We know that this message was added during the development of Windows 1.0, and removed prior to the official release of 1.01, so we should be able to find it inside the WINDOWS.H of a pre-release Windows 1.0 build. Windows 1.0 Alpha Release is the earliest build to communicate with applications by passing Windows Messages, so let’s take a look at its WINDOWS.H.

#define WM_NULL             0x0000
#define WM_CREATE           0x0001
#define WM_DESTROY          0x0002
#define WM_SIZE             0x0003
#define WM_SETVISIBLE       0x0004
#define WM_ENABLE           0x0005
#define WM_SETREDRAW        0x0006
#define WM_SETTEXT          0x0007
#define WM_GETTEXT          0x0008

0x4 appears to be WM_SETVISIBLE, which later became Windows Message 0x9, and is not what we want (it was later removed as well, but that is a different story). The next publicly released build is Beta Release, and in its WINDOWS.H we can see:

#define WM_NULL             0x0000
#define WM_CREATE           0x0001
#define WM_DESTROY          0x0002
#define WM_MOVE             0x0003
#define WM_SIZEWAIT         0x0004
#define WM_SIZE             0x0005
#define WM_ACTIVATE         0x0006
#define WM_SETFOCUS         0x0007
#define WM_KILLFOCUS        0x0008

Almost identical to modern WINUSER.H, but this time we see WM_SIZEWAIT being defined as 0x4. Since everything else are identical, it is safe refer to Windows Message 0x4 as WM_SIZEWAIT now. Since WM_SIZEWAIT cannot be found in Alpha Release’s WINDOWS.H, we can say it was introduced in Beta Release.

As mentioned above, it was never documented, so the next step is to figure out its purpose and document it. To do that, the best way is to write a program. Since Beta Release is the earliest pre-release build with WM_SIZEWAIT defined, it is a good idea to see what triggers Windows to send that message in Beta Release.

Luckily, Xelloss wrote an app for that already, so we don’t have to write anything. Basically, you need to add WM_SIZEWAIT to the switch statement in your WndProc, and notify yourself in some way upon receiving it. Xelloss’ app beeps every time WM_SIZEWAIT is received, and prints out the wParam and lParam.

Xelloss’ application

Since it is most likely size-related due to its name, we need to monitor what happens when we resize an application. When an application is resized in Windows 1.0, some/all applications on the screen must also be resized, due to tiling. By performing various windowing operations, we can see that Windows sends it to a window that is about to be resized, but only in certain conditions: for instance, if window A is resized and, as a consequence, the size of another window B has to change, WM_SIZEWAIT will be sent to B, but not to A. Basically, WM_SIZEWAIT is sent to the window that is about to be resized as a consequence of another window being resized.

So, what are the differences between the mysterious WM_SIZEWAIT and the well-documented WM_SIZE? This table is all you need to answer that question:

ActionWM_SIZEWM_SIZEWAIT
Create this windowYesNo
Resize this windowYesNo
Minimize this windowYesNo
Restore this windowYesYes
Resize another window
causing this window to
be resized
YesYes
Swap this window with
another window of the
same size
YesNo
Swap this window with
another window of a
different size
YesNo
Swap another window
of the same size with
this window
YesYes
Swap another window
of a different size with
this window
YesYes
WM_SIZE vs. WM_SIZEWAIT

Why did Microsoft remove it? Nobody knows. I believe there is no point in telling a window that it will be resized because the user resized another window, as you can see, WM_SIZE will be sent regardless. Maybe Microsoft removed it as a result of its uselessness, maybe not. Hopefully Raymond Chen will tell us one day.