许多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. Visual Studio2015 简体中文版 安装

    VS2015简体中文版安装 导航 介绍 解决安装先决条件 安装 VS2015 创建桌面快捷方式 启动 VS2015 命令启动VS2015 配置 VS2015 启动完成 Visual Studio的功能 ...

  2. Tomcat从零开始(十七)——StandardWrapper

    第十七课:StandardWrapper 课前复习: 不知道大家是否还有印象,就是在6.7节课说的4种container,粗略的从大到小来说就是engine,host,context,和wrapper ...

  3. windows下流媒体nginx-rmtp-module服务器搭建及java程序调用fmpeg将rtsp转rtmp直播流【转】

    https://github.com/illuspas/nginx-rtmp-win32 http://bashell.sinaapp.com/archives/build-nginx-rtmp-mo ...

  4. CosmosEngine - Unity3D /2D 轻量级游戏开发框架

    CosmosEngine https://github.com/mr-kelly/CosmosEngine 快速入门 简介 特性 约定 整体架构图 使用经验 工作流 未来功能 快速入门 1.将NGUI ...

  5. 一个用于将sql脚本转换成实体类的js代码

    以前写过一段C#,苦于编译才能用.这样的小工具最好是用脚本语言来编写,易于执行,也易于修改. js 代码 convert.js ------------------------------------ ...

  6. Android中Word转Html

    一.POI方式 1.先看word效果图 2.再看下在android上使用WebView显示的效果   3. 生成的html的代码,如下: <html> <head> <M ...

  7. O2O研究系列——O2O知识思维导图整理

    本篇文章对O2O电子商务模式的常规知识点,使用思维导图的方式整理,表达的形式是名词纲领性的方式, 不会在图中详细说明各个点. 通过这个图研究O2O模式时,可以系统的对各个业务点进行更深入的研究,避免有 ...

  8. easy ui datagrid 数据分页

    参照easyui官方网站提供的demo写了个datagrid数据分页的demo, 具体参数我就不一一罗列了,详细见官方网站, 这里只介绍一下具体的注意事项和常乃用到的几项, $('#test').da ...

  9. PHP 常用函数回顾

    array_change_key_case — 返回字符串键名全为小写或大写的数组array_chunk — 将一个数组分割成多个array_combine — 创建一个数组,用一个数组的值作为其键名 ...

  10. 自己动手写CPU之第五阶段(2)——OpenMIPS对数据相关问题的解决措施

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版).今天是第16篇.我尽量每周四篇 5.2 OpenMIPS对数据相关问题的解决措施 OpenMIPS处理器採用数据前推的方法来解决流水 ...