许多Windows程序开发者喜欢使用WM_COPYDATA来实现一些进程间的简单通信(笔者也正在学习共享内存的一些知识来实现一些更高级的通信),这篇文章描述了笔者在使用这项技术时候的一些总结以及所遇到的一个问题回顾和分析。

进程通讯的相关知识

在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。常用的方法有

  1. 使用内存映射文件
  2. 通过共享内存DLL共享内存
  3. 使用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可以是子进程和父进程都认识的对象

  1. WM_COPYDATA消息只可以使用SendMessage函数(同步)发送,不可以使用PostMessage(异步)发送,笔者认为可能是异步形式下无法保证指向数据的指针可以正确的释放。
  2. COPYDATA结构体的实质依然是共享内存,区别是这一片特殊的共享内存由操作系统管理而不用用户手动申请管理。
  3. 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方式反思,回顾和总结的更多相关文章

  1. 【转】 Linux/Unix 进程间通信的各种方式及其比较

    http://blog.csdn.net/guopengzhang/article/details/5528260 进程间通信就是在不同进程之间传播或交换信息,那么不同进程之间存在着什么双方都可以访问 ...

  2. 使用缓存(Cache)的几种方式,回顾一下~~~

    前言 如今缓存成为了优化网站性能的首要利器,缓存使用的好,不仅能让网站性能提升,让用户体验变好,而且还能节约成本(增加一台缓存服务器可能就节约好几台机器):那平时小伙伴们都使用哪些缓存方式呢?这里就来 ...

  3. 进程间通信的WM_COPYDATA的使用

    http://blog.csdn.net/ao929929fei/article/details/6316174 接收数据的一方 ON_WM_COPYDATA() afx_msg BOOL OnCop ...

  4. 1004 Counting Leaves 对于树的存储方式的回顾

    一种新的不使用左右子树递归进行树高计算的方法,使用层次遍历 树的存储方式: 1.本题提供的一种思路: 使用(邻接表的思想)二维数组(vector[n])表示树,横坐标表示 父节点,每一行表示孩子. 能 ...

  5. Linux -- 进程间通信几种方式的总结

    管道 优点 管道文件不占磁盘空间,打开管道时在内存中分配空间: 管道读端会在读取完管道内数据后自动进入阻塞,直到写端再次写入数据: 缺点 管道是半双工的,数据只能从一个方向上流动: 管道大小 PIPE ...

  6. linux 进程间通信方式

    管道: 它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信消息队列: 用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中 ...

  7. CE 进程间通信

    WINCE下进程间通信常用的方式有:剪贴板(Clipboard),网络套接字(Socket),WM_COPYDATA消息,共享内存,管道(消息队列),注册表等 剪贴板 //////////////// ...

  8. WINCE下进程间通信(一)

    WINCE下进程间通信(一) 在WINCE开发中经常需要在不同的进程之间传递.共享数据,总结了一下,WINCE下进程间通信常用的方式有:Windows消息,共享内存,socket通信,管道,全局原子, ...

  9. 孙鑫MFC学习笔记17:进程间通信

    17 1.进程间通信4种方式 2.OpenClipboard打开剪贴板 3.EmptyClipboard清空剪贴板,并把所有权分配给打开剪贴板的窗口 4.SetClipboardData设置剪贴板数据 ...

随机推荐

  1. My SqL 常用命令

    1:使用SHOW语句找出在服务器上当前存在什么数据库:mysql> SHOW DATABASES;2:2.创建一个数据库MYSQLDATAmysql> CREATE DATABASE MY ...

  2. 如何使用WinDriver为PCIe采集卡装驱动

    如何使用WinDriver为PCIe采集卡装驱动 第一步:使用WinDriver生成驱动 1.运行Drier Wizard 2.点击New host driverproject 3.在列表中,选择待安 ...

  3. Remove all your local git branches but keep master

    Sometimes after a sprint, all the remaining branches are just taking up space. Here's a small snippe ...

  4. Android 解决qq分享后返回程序出现的Bug

    问题:当我们使用qq分享时,分享成功后选择留在qq,这个时候按home键,回到手机主界面,在点击回到我的app,这个时候会出现界面显示出来了,但是任何事件都不响应,即按钮没反应. 分析:这个时候回到我 ...

  5. Linux下好用的命令

    split -l 10000 articles.json  将文件按行分成多个文件

  6. 如何利用ps去批量完成一套任务

    作为前端开发人员,不说设计你会不会,ps作为一个工具来说,前端开发人员还是要熟悉才行的 做了一个项目,客户自己上传了图片,他表示上传非常慢,我们表示不解,为何那么慢,网络问题吗,经过看了她的图片,发现 ...

  7. react 设置代理(proxy) 实现跨域请求

    一,对于使用creat-react-app构建的项目,可以直接在package.json下配置,具体如下 "proxy": "http://api.xxxx.com&qu ...

  8. python冒泡算法

    array = [1,2,3,6,5,4,28,2,1,9,3,0,200,2,9,3,2,98,33,988,22,0,223,0,33,78,222,88,32,0,238,883,2,0,23] ...

  9. CentOS下febootstrap自制Docker的CentOS6.6和7.1 Docker镜像

    docker image centos febootstrap CentOS 6.6和7.1 Docker自制CentOS镜像 安装: ? 1 yum -y install febootstrap 添 ...

  10. 默认权限umask

    什么是umask? 当我们登录系统之后创建一个文件总是有一个默认权限的,那么这个权限是怎么来的呢?这就是umask干的事情.umask设置了用户创建文件的默认 权限,它与chmod的效果刚好相反,um ...