许多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. vue - 选项

    1.计算属性(computed):主要是对原数据进行改造输出.改造输出:包括格式化数据(价格,日期),大小写转换,排序,添加符号 2.methods(methods):用于绑定html中的事件对应的方 ...

  2. RxJava异步请求加载状态控制

    在我看来,RxJava最大的特点就是异步,无论你是解析复杂的数据或是IO操作,我们都可以利用它内置的线程池进行线程间的调度,简单的使用 subscribeOn(Schedulers.io()).doO ...

  3. jsp实现仿QQ空间新建多个相冊名称,向相冊中加入照片

    工具:Eclipse,Oracle,smartupload.jar.语言:jsp,Java:数据存储:Oracle. 实现功能介绍: 主要是新建相冊,能够建多个相冊,在相冊中加入多张照片,删除照片,删 ...

  4. C++ STL中Map的按Value排序

    那么我们如何实现对pair按value进行比较呢? 第一种:是最原始的方法,写一个比较函数:  第二种:刚才用到了,写一个函数对象.这两种方式实现起来都比较简单. typedef pair<st ...

  5. HDU 1073 Online Judge(字符串)

    Online Judge Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Tot ...

  6. XAudio2学习之混音

    XAudio2不仅能够进行採样率转换.还能够进行混音.所谓混音就是将多路音频混合成一路进行输出.混音主要是IXAudio2SubmixVoice进行此项功能. 数据由IXAudio2SourceVoi ...

  7. 程序猿的职场潜意识Top10

    什么叫潜规则?事实上就是不明文规定的一些规则,关键是他没法明文规定,由于有的规则太没节操. 在我们职场中有些规则你不遵守将举步艰难.而要玩转这些潜规则.那么你必需要具备例如以下10个潜意识: 1.项目 ...

  8. OJ刷题---简单password破解

    题目要求: 输入代码: #include<iostream> #include <cstdio> #include <cstring> using namespac ...

  9. 10-spring学习-注入Resource

    注入Resource 虽然Resource 的子类利用了字符串格式进行了隐藏,但是此时的代码中,ResourceLoader跟我的开发没有任何关系, 如果真的开发只关心Resource一个接口就够了. ...

  10. Hibernate映射set与List

    1.对于set类型,如果集合中的元素是简单地类型,如字符串型,set使用另外一种映射方式: team类: import java.util.HashSet; import java.util.Set; ...