此文章由原博客迁移而来。

原文地址:https://blog.betaworld.cn/183

原作者:BetaRookie

以下为本文正文部分。

183-1-Archaeology.png

以前开发过 Windows 应用程序的人应该知道一种叫做 Windows Message 的东西。Windows 通过向应用程序窗口传递消息与应用程序窗口通信。消息是一个指定特定事件的数字代码。例如,如果用户按下鼠标左键,窗口将收到一条包含以下消息代码的消息:

#define WM_LBUTTONDOWN 0x0201
为了向窗口传递消息,操作系统调用为该窗口注册的窗口过程(WndProc)。Windows Message 在头文件 WINUSER.H(在 Windows 的最新版本中)或 WINDOWS.H 中定义,下面是其中的一个摘录:

#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

通过幼儿园数学考试的人应该很快意识到一件事 — 没有东西被定义为 0x4。为什么?因为它在很久以前就从 WINUSER.H / WINDOWS.H 中删除了。有人说,一旦有东西被添加到 WINDOWS.H 中,它将永远留在那里,这显然是个例外。又为什么?因为用于发送 Windows Message 0x4 的代码在 Windows 1.0 正式发布之前就被删除了(在 Premiere Edition 和 RTM 之间的某个时间点)。因此,它从未被记录,但它在 WINDOWS.H 中保留了几年(这表明微软非常关心兼容性)。

现在,让我们解开谜团。我们需要先找出神秘的 Windows Message 0x4 的名称。我们知道,此消息是在 Windows 1.0 开发期间添加的,在正式发布 1.01 之前删除的,因此我们应该能够在预发布的 Windows 1.0 版本的 WINDOWS.H 中找到它。Windows 1.0 Alpha Release 是通过传递 Windows Message 与应用程序通信的最早版本,所以让我们看看它的 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 似乎是 WM_SETVISIBLE,后来变成了 Windows Message 0x9,这不是我们想要的(它后来也被删除了,但情况不同)。下一个公开发布的版本是 Beta Release,在其 WINDOWS.H 中,我们可以看到:

#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

几乎与现代 WINUSER.H 相同,但这次我们看到 WM_SIZEWAIT 被定义为 0x4。由于所有其他内容都相同,因此现在可以安全地将 Windows Message 0x4 称为 WM_SIZEWAIT。由于在 Alpha Release 的 WINDOWS.H 中找不到 WM_SIZEWAIT,我们可以说它是在 Beta Release 中引入的。

如上所述,它从未被记录,因此下一步是找出它的目的并记录下来。要做到这一点,最好的方法是编写一个程序。由于 Beta Release 是最早定义了 WM_SIZEWAIT 的预发布版本,因此最好看看是什么触发了 Windows 在 Beta Release 中发送该消息。

幸运的是,Xelloss 已经为此编写了一个应用程序,所以我们不必编写任何东西。基本上,您需要将 WM_SIZEWAIT 添加到 WndProc 中的 switch 语句中,并在收到它时以某种方式通知您自己。Xelloss 的应用程序在每次收到 WM_SIZEWAIT 时都会发出嘟嘟声,并显示 wParamlParam

183-2-Size-Spy-WM_SIZEWAIT.png

由于它的名称很可能与调整大小有关,因此我们需要监视调整应用程序大小时发生的情况。在 Windows 1.0 中调整应用程序的大小时,由于平铺,屏幕上的某些/所有应用程序也必须调整大小。通过执行各种窗口操作,我们可以看到 Windows 将其发送到即将调整大小的窗口,但仅在某些情况下:例如,如果窗口 A 调整了大小,因此另一个窗口 B 的大小必须更改,WM_SIZEWAIT 将发送到 B,而不是 A。基本上,WM_SIZEWAIT 会被发送到将要因为另一个窗口大小调整而被调整大小的窗口。

那么,神秘的 WM_SIZEWAIT 和众所周知的 WM_SIZE 之间有什么区别呢?此表是回答该问题所需的全部内容:

行为WM_SIZEWM_SIZEWAIT
创建此窗口
调整此窗口的大小
最小化此窗口
恢复此窗口
调整另一个窗口的 大小,从而调整此 窗口的大小
将此窗口与另一个 相同大小的窗口交 换
将此窗口与其他大 小的窗口交换
将另一个相同大小 的窗口与此窗口交 换
将另一个不同大小 的窗口与此窗口交 换

为什么微软要删除它?没人知道。我相信告诉一个窗口它将被因为用户调整了另一个窗口的大小而被调整大小是没有意义的,正如您所看到的,无论如何 WM_SIZE 都将被发送。也许微软删除它是因为它没有用处,也许不是。希望有一天 Raymond Chen 会告诉我们。

183-1-Archaeology.png

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.

183-2-Size-Spy-WM_SIZEWAIT.png

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 resizedYesYes
Swap this window with another window of the same sizeYesNo
Swap this window with another window of a different sizeYesNo
Swap another window of the same size with this windowYesYes
Swap another window of a different size with this windowYesYes

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.

标签: API, mystery, Windows API