翻译《The Old New Thing》- If control-specific messages belong to the WM_USER range, why are messages...

If control-specific messages belong to the WM_USER range, why are messages like BM_SETCHECK in the system message range? - The Old New Thing (microsoft.com)icon-default.png?t=N7T8https://devblogs.microsoft.com/oldnewthing/20070910-00/?p=25203

Raymond Chen 2007年09月10日


如果控件特定消息属于WM_USER范围,为什么像BM_SETCHECK这样的消息在系统消息范围内?

简要

文章讨论了为何一些控制特定消息如BM_SETCHECK属于系统消息范围,而通常这些应属于WM_USER范围。原因是为了避免在32位Windows中因地址空间隔离导致跨进程通信时的崩溃问题,所有内置控件的遗留消息被移到系统消息类别,窗口管理器可以正确处理这些消息。

正文

        当我讨论消息编号属于谁时, 你可能已经注意到,即使是针对编辑框、按钮、列表框、组合框、滚动条和静态控件的控制特定消息, 它们也属于系统范围,尽管它们是控制特定的。 这些消息是如何最终出现在那儿的呢?

        它们一开始并不在那里。

        在16位Windows中,这些控制特定消息位于控制特定消息范围, 正如你所期望的那样。

#define LB_ADDSTRING      (WM_USER + 1)
#define LB_INSERTSTRING   (WM_USER + 2)
#define LB_DELETESTRING   (WM_USER + 3)
#define LB_RESETCONTENT   (WM_USER + 5)
#define LB_SETSEL         (WM_USER + 6)
#define LB_SETCURSEL      (WM_USER + 7)
#define LB_GETSEL         (WM_USER + 8)
#define LB_GETCURSEL      (WM_USER + 9)
#define LB_GETTEXT        (WM_USER + 10)
...

        想象一下,如果在过渡到Win32时保留了这些消息编号, (给你时间发挥你的想象力。)

        这里有一个提示。 由于16位Windows让所有程序在同一个地址空间运行, 程序可以这样做:

char buffer[100];
HWND hwndLB = <a list box that belongs to another process>
SendMessage(hwndLB, LB_GETTEXT, 0, (LPARAM)(LPSTR)buffer);

 

        这读取了属于另一个进程的列表框中的项文本。 由于进程在同一个地址空间运行,发送进程中的缓冲区地址在接收进程中也是有效的, 所以当接收的列表框将结果复制到缓冲区时,一切都可以正常工作。

        现在回想一下,如果在过渡到Win32时保留了这些消息编号, (再次给你时间发挥你的想象力。)

        考虑一个32位程序,它执行的代码片段与上面的代码片段完全相同。 当程序从16位移植到32位代码时,代码可能没有做任何改变, 因为它没有产生任何编译器警告, 因此没有引起任何需要特别处理的注意。

        但由于在Win32中,进程在不同的地址空间运行, 程序现在崩溃了。 更准确地说,它崩溃了那个其他程序, 因为它试图将文本复制到它认为是有效缓冲区的指针, 但实际上是指向错误地址空间的指针。

        这正是你想要的。 一个完全合法的程序因为别人的bug而崩溃。 如果你幸运的话,程序员会在测试中发现这个bug, 但他们怎么知道问题出在哪里,因为他们的程序没有崩溃; 崩溃的是另一个程序!

        如果你不幸运,这个bug会在测试中溜过去, (例如,它可能在一个很少执行的代码路径中) 而最终用户的体验是 “Microsoft Word随机崩溃。真是垃圾。” (实际上,崩溃是由另一个完全不同的程序引起的。)

        为了避免这个问题,所有内置于窗口管理器的“遗留”消息都被移动到了系统消息类别。 这样,当你发送消息0x0189时,窗口管理器知道它是LB_GETTEXT并且可以为你进行参数封送。 如果它留在WM_USER范围, 窗口管理器在收到消息0x040A时就不会知道该怎么做, 因为那可能是LB_GETTEXT, 也可能是TTM_HITTESTATBM_SETSEL, 或者是其他一些控制特定消息。

        理论上,这种移动只需要对遗留消息进行; 即,在16位Windows中存在窗口消息。 (注意Windows 95增加了一些新的16位消息, 所以这种重新映射至少需要持续到Windows NT 4的shell更新发布。) 尽管如此,窗口管理器团队甚至在系统消息范围内添加了*_GET*INFO消息, 尽管从兼容性角度来看没有必要将它们放在那里。 我的猜测是,这样做是为了使辅助工具的工作更容易。

        但请注意,将新消息放在系统消息范围是例外而不是规则, 对于编辑框和其他“核心”控件来说。 例如,新消息EM_SETCUEBANNER的数值为0x1501, 这已经很好地进入了WM_USER范围。 如果你尝试在不采取必要预防措施的情况下跨进程发送此消息, 你将崩溃目标进程。

注: 标准免责声明适用。 我将不再在未来的文章中重复这个免责声明。

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0x0007

可不可奖励我吃只毛嘴鸡 馋😋

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值