进程间通信之WM_COPYDATA方式反思,回顾和总结
许多Windows程序开发者喜欢使用WM_COPYDATA来实现一些进程间的简单通信(笔者也正在学习共享内存的一些知识来实现一些更高级的通信),这篇文章描述了笔者在使用这项技术时候的一些总结以及所遇到的一个问题回顾和分析。
进程通讯的相关知识
在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。常用的方法有
- 使用内存映射文件
- 通过共享内存DLL共享内存
- 使用SendMessage向另一进程发送WM_COPYDATA消息
比起前两种的复杂实现来,WM_COPYDATA消息无疑是一种经济实惠的一种方法
WM_COPYDATA的相关知识
typedef struct tagCOPYDATASTRUCT {
ULONG_PTR dwData; //用户定义数据
DWORD cbData; //用户定义数据的长度
__field_bcount(cbData) PVOID lpData; //指向用户定义数据的指针
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
COPYDATASTRUCT结构是WM_COPYDATA的参数,在这里可以指定对象的内存地址,对象的长度,data可以是子进程和父进程都认识的对象
- WM_COPYDATA消息只可以使用SendMessage函数(同步)发送,不可以使用PostMessage(异步)发送,笔者认为可能是异步形式下无法保证指向数据的指针可以正确的释放。
- COPYDATA结构体的实质依然是共享内存,区别是这一片特殊的共享内存由操作系统管理而不用用户手动申请管理。
- WM_COPYDATA适合小数据量的进程间通信,大数据量可能造成内存问题,以及界面卡死,因为消息的发送形式是同步的。
WM_COPYDATA运用在进程通信
一个简单的发送方和接收方的代码(C++实现):
发送端
void CSendDlg::OnDataSend()
{
CWnd *pWnd = CWnd::FindWindow(NULL,"接收窗口的标题"); CString sCopyData = "HELLO";
COPYDATASTRUCT cpd;
cpd.dwData = 0;
cpd.cbData = sCopyData.GetLength() + 1;//多加一个长度,防止乱码
cpd.lpData = (void*)sCopyData.GetBuffer(cpd.cbData);
pWnd->SendMessage(WM_COPYDATA,NULL,(LPARAM)&cpd);
}
接收端(需要根据实际情况做一些修改,下面的文章会更新)
// 声明
afx_msg void OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct);
// 添加消息映射
BEGIN_MESSAGE_MAP(CMainFrame, CCJMDIFrameWnd)
...
...
ON_MESSAGE(WM_COPYDATA, OnCopyData)
...
...
END_MESSAGE_MAP() //函数实现
BOOL CReceiveDlg::OnCopyData( CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct )
{
AfxMessageBox((LPCSTR)(pCopyDataStruct->lpData));
}
WM_COPYDATA使用过程中遇到的一个问题
在使用了类似上述代码时,笔者遇到一个问题,如果使用CString类型传递数据时,在接受方的窗体内,始终只能接受到第一个字母,既如果发送端发送“HELLO”,接收端只能收到“H”。
原因:
单步调试的时候发现传进去的其实是L"HELLO",这表示“HelloWorld”将以unicode形式编码,Visual Studio默认默认采用unicode编码

客户端读取的方式是LPTSTR,LPTSTR被定义成是一个指向以NULL(‘\0’)结尾的16位ANSI字符数组指针,而接受到的数据是一个以Unicode编码的32位双字节字符数组指针。这必然将产生截断的问题
Unicode使用两个字节表示一个字符,字符'H'的Unicode编码就是0x0072,此时“HELLO”在内存中的表现形式是:
72 00 - ‘H’
69 00 - ‘E'
76 00 - ‘E'
76 00 - ‘E'
79 00 - 'O'

(注:基于X86平台的PC是基于Little Endian既将低字节存储在起始地址,故有如上地址分布)
因为x86CPU是little-endian,值0x0072在内存中的存储形式是72 00。你能看出如果这个字符串被传给strlen()函数会出现什么问题吗?它将先看到第一个字节42,然后是00,而00是字符串结束的标志,于是 strlen()将会返回1。所以如果用LPTSTR既1个字节对齐的方式去读取数据,只能得到首字母H
解决方法:
遇到类似的问题,一定需要了解字符的编码方式,然后用正确的方式去读取,在这里应该用微软所提供的LPWSTR既指向宽字符的指针去读取该字符串方可以得到正确的输出:
CString sCopyData = (LPWSTR)(pCopyDataStruct->lpData);
注:LPWSTR可使用上述方式显示转换为CString
反思:
字符的编码方式一定要理解清楚,否则将会带来难以发现的bug
以上是笔者粗略的理解,如果有不当之处欢迎指正。
进程间通信之WM_COPYDATA方式反思,回顾和总结的更多相关文章
- 【转】 Linux/Unix 进程间通信的各种方式及其比较
http://blog.csdn.net/guopengzhang/article/details/5528260 进程间通信就是在不同进程之间传播或交换信息,那么不同进程之间存在着什么双方都可以访问 ...
- 使用缓存(Cache)的几种方式,回顾一下~~~
前言 如今缓存成为了优化网站性能的首要利器,缓存使用的好,不仅能让网站性能提升,让用户体验变好,而且还能节约成本(增加一台缓存服务器可能就节约好几台机器):那平时小伙伴们都使用哪些缓存方式呢?这里就来 ...
- 进程间通信的WM_COPYDATA的使用
http://blog.csdn.net/ao929929fei/article/details/6316174 接收数据的一方 ON_WM_COPYDATA() afx_msg BOOL OnCop ...
- 1004 Counting Leaves 对于树的存储方式的回顾
一种新的不使用左右子树递归进行树高计算的方法,使用层次遍历 树的存储方式: 1.本题提供的一种思路: 使用(邻接表的思想)二维数组(vector[n])表示树,横坐标表示 父节点,每一行表示孩子. 能 ...
- Linux -- 进程间通信几种方式的总结
管道 优点 管道文件不占磁盘空间,打开管道时在内存中分配空间: 管道读端会在读取完管道内数据后自动进入阻塞,直到写端再次写入数据: 缺点 管道是半双工的,数据只能从一个方向上流动: 管道大小 PIPE ...
- linux 进程间通信方式
管道: 它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信消息队列: 用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中 ...
- CE 进程间通信
WINCE下进程间通信常用的方式有:剪贴板(Clipboard),网络套接字(Socket),WM_COPYDATA消息,共享内存,管道(消息队列),注册表等 剪贴板 //////////////// ...
- WINCE下进程间通信(一)
WINCE下进程间通信(一) 在WINCE开发中经常需要在不同的进程之间传递.共享数据,总结了一下,WINCE下进程间通信常用的方式有:Windows消息,共享内存,socket通信,管道,全局原子, ...
- 孙鑫MFC学习笔记17:进程间通信
17 1.进程间通信4种方式 2.OpenClipboard打开剪贴板 3.EmptyClipboard清空剪贴板,并把所有权分配给打开剪贴板的窗口 4.SetClipboardData设置剪贴板数据 ...
随机推荐
- PHP上传文件功代码练习(单文件)
前端: <html> <head><title>upload file</title> <meta http-equiv="Conten ...
- ffplay的快捷键以及选项 FFmpeg 基本用法 FFmpeg常用基本命令 ffmpeg常用转换命令,支持WAV转AMR
ffmpeg -i 32_mkv_h264_718x480_ac3.mkv -codec copy -bsf:v h264_mp4toannexb -f mpegts xx.ts ./ffmpeg ...
- 改动Dialog窗口的类名
VS2013 的MFC project(project名: MobileLink).想要改动窗口的类名时,发现不是像设置窗口名一样调用一个函数能够实现的. 实现的注意问题,请看凝视. (1) 改 ...
- 改造的unity3d文件打包脚本
ExportAssetBundles.rar // C# Example // Builds an asset bundle from the selected objects in the proj ...
- 使用jq Deferred防止代码被回调函数分解分解的支离破碎
//移动人物 function moveInterval(stopPosotion){ var dtd = $.Deferred(); // 生成Deferred对象 var yidong= wind ...
- 补习知识:Entity Framework Code First属性映射约定
Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...
- Android_JarZip压缩和解压文件
本文资料来自<android开发权威指南> AndroidSDK中提供了java.util.jar和java.util.zip包中的若干类和接口来完成. 压缩文件基本步骤: 1.创 ...
- 【HTML5 WebSocket】WebSocket对象特性和方法
<HTML5 WebSocket权威指南>学习笔记&3 WebSocket方法的对象特性 1. WebSocket方法 a. send方法 send方法用于在WebSocket连接 ...
- 检查SSD磁盘是否开启了TRIM指令
fsutil behavior QUERY DisableDeleteNotify 如果查询结果是“DisableDeleteNotify = 0”,代表SSD已经支持并启用Trim指令:如果提示为“ ...
- JavaScript正则表达式基础知识汇总
一.创建正则对象: 1.构造函数RegExp创建正则对象 var pattern = new RegExp('s$'); //pattern匹配以s结尾的字符串 2.使用正则直接量 var patte ...