为Windows 3.1改造WM_COPYDATA消息
摘要
本文解释如何利用共享地址空间,将针对32位Windows引入的WM_COPYDATA消息改造到16位Windows 3.1上,使得16位程序间的通信无需任何更改。
<p>不久前,我讨论了<a title="I can use WM_COPYDATA to send a block of data to another window, but how does it send data back?" href="https://devblogs.microsoft.com/oldnewthing/20251114-00/?p=111792">如何从<code>WM_<wbr />COPYDATA</code>消息返回结果</a>。这让我想起了一段巧妙的历史。</p>
<p><code>WM_<wbr />COPYDATA</code>消息是在32位Windows中引入的。在16位Windows中不需要它,因为所有16位程序都在同一个地址空间中运行。一个进程中的远指针在任何进程中都有效。你可以将其放在窗口消息的<code>lParam</code>中,并发送给任何其他窗口,无论是同一进程还是不同进程,都没关系。但是32位程序运行在独立的地址空间中,因此这个技巧不再适用。因此需要<code>WM_<wbr />COPYDATA</code>来传递数据,不仅在32位程序之间,还在32位程序和16位程序之间。</p>
<p>这个消息是如何被改造到16位Windows中,以便Win32s能够支持它的?</p>
<p>很简单:它已经在无意中被实现了。</p>
<p>如果源窗口和目标窗口都是16位窗口,那么指向<code>COPYDATASTRUCT</code>的指针在两个进程中已经有效,<code>COPYDATASTRUCT</code>内部的指针也是。并且<code>wParam</code>中的窗口句柄对于两个进程也是相同的。因此,不对<code>wParam</code>和<code>lParam</code>做任何处理,只是让它们从16位程序传递到另一个16位程序,仍然会按预期工作。</p>
<p>而恰好Windows 3.1已经这样做了:Windows 3.1总是原封不动地传递<code>wParam</code>和<code>lParam</code>,即使消息发送者和接收者位于不同的进程中,因为所有程序共享相同的地址空间。</p>
<p>这只是一种巧妙的设计:将<code>WM_<wbr />COPYDATA</code>消息设计成当在16位程序之间发送时,空编组器就是正确的行为。</p>
<p>本文《<a href="https://devblogs.microsoft.com/oldnewthing/20260616-00/?p=112430">为Windows 3.1改造<code>WM_<wbr />COPYDATA</code>消息</a>》最先出现在<a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>上。</p>
查看缓存全文
缓存时间: 2026/06/17 12:54
# 将 WM_COPYDATA 消息反向移植到 Windows 3.1 - 《The Old New Thing》
来源:https://devblogs.microsoft.com/oldnewthing/20260616-00?p=112430
不久前,我讨论过如何从 `WM_COPYDATA` 消息返回结果(https://devblogs.microsoft.com/oldnewthing/20251114-00/?p=111792)。这让我想起了一段巧妙的历史。
`WM_COPYDATA` 消息是在 32 位 Windows 中引入的。16 位 Windows 并不需要它,因为所有 16 位程序都运行在同一个地址空间中。一个远指针在一个进程中有效,在所有进程中也同样有效。你可以把它放在窗口消息的 `lParam` 中,发送给任何其他窗口,无论是同一进程还是不同进程,都没有关系。但 32 位程序运行在独立的地址空间中,因此这个技巧行不通。于是就需要 `WM_COPYDATA` 来在 32 位程序之间以及 32 位程序与 16 位程序之间传递数据。
那么,这个消息是如何被反向移植到 16 位 Windows 中,以便 Win32s 能够支持它的呢?
很简单:它实际上已经被实现了,只是无意中而已。
如果源窗口和目标窗口都是 16 位窗口,那么指向 `COPYDATASTRUCT` 的指针在两个进程中都已经有效,`COPYDATASTRUCT` 内部的指针也是如此。`wParam` 中的窗口句柄在两个进程中也相同。因此,对 `wParam` 和 `lParam` 不做任何处理,只是简单地让它们从 16 位程序传递到另一个 16 位程序,仍然会按预期工作。
而恰好 Windows 3.1 已经做到了这一点:Windows 3.1 总是原封不动地传递 `wParam` 和 `lParam`,即使消息发送者和接收者位于不同的进程中,也是如此,因为所有程序共享同一个地址空间。
这只是一个巧妙的把戏:设计 `WM_COPYDATA` 消息时,使得在 16 位程序之间发送时,空编组器就是正确的行为。
### 分类
### 主题
## 作者
Raymond Chen
Raymond 参与 Windows 的发展已有 30 多年。2003 年,他创办了一个名为 The Old New Thing 的网站,其受欢迎程度远超他最疯狂的想象,这一发展至今仍让他感到不安。该网站还衍生出一本书,巧合的是书名也叫《The Old New Thing》(Addison Wesley 2007 年出版)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账号上,讲述一些毫无用处的故事。
相似文章
关于滥用Windows窗口类额外字节的兼容性说明
Raymond Chen 讨论了一个历史性的 Windows 兼容性问题,其中一些 16 位程序滥用窗口类额外字节来存储私有数据,以及微软如何在保持向后兼容性的同时,对 32 位和 64 位程序堵住了这个漏洞。
Windows中窗口和类额外字节的演变
本文追溯了Windows中窗口和类额外字节从16位到32位再到64位的演变,详细描述了随着句柄和指针的扩展,函数名称和数据大小的变化。
Windows 3.1 平台上的 Visual Basic
关于 Visual Basic 在 Windows 3.1 上兴衰的回顾,探讨比尔·盖茨的雄心以及微软在个人计算领域的霸主地位。
Windows NT 3.1 的优缺点
回顾 Windows NT 3.1,这是微软首个完全32位的操作系统,探讨其优点(稳定性、安全性、可移植性、NTFS)和缺点(硬件要求高、软件支持有限)。
如何在无缓冲模式下执行 CopyFile?
说明了如何将 COPY_FILE_NO_BUFFERING 标志与 CopyFileEx 和 CopyFile2 一起使用,以使用无缓冲句柄复制文件,以及为什么在句柄打开后无法使用进度回调来更改缓冲模式。