Windows下进程通信方式[转]

摘要:
本文通过共享内存来实现进程之间的数据交换,并利用Windows消息机制来实现进程间的同步。这两种机制的结合不仅解决了交换数据量小的问题,还解决了进程并发访问数据的问题。
一、引言
在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。WIN32 API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换.
内部进程间通讯(即:同机通讯)和数据交换有多种方式:消息、共享内存、匿名(命名) 管道、邮槽、Windows套接字等多种技术。“共享内存”(shared memory)可以定义为对一个以上的进程是可见的内存或存在于多个进程的虚拟地址空间。例如:如果两个进程使用相同的DLL,只把DLL的代码页装入内 存一次,其他所有映射这个DLL的进程只要共享这些代码页就可以了;利用消息机制实现IPC虽然有交换的数据量小、携带的信息少等缺点,但由于其实现方 便、应用灵活而广泛应用于无须大量、频繁数据交换的内部进程通讯系统之中。本文通过共享内存实现进程间的数据交换,利用windows消息机制实现进程间 的同步,两种机制结合使用,不仅解决了交换数据量小的问题,还解决了进程并发存取数据的问题。
二、同机进程间共享内存的实现
采用内存映射文件实现WIN32进程间的通讯:Windows中的内存映射文件的机制为我们高效地操作文件提供了一种途径,它允许我们在WIN32进程中保留一段内存区域,把目标文件映射到这段虚拟内存中。在程序实现中必须考虑各进程之间的同步问题。具体实现步骤如下:
1、在服务器端进程中调用内存映射API函数CreateFileMapping创建一个有名字标识的共享内存;
函数CreateFileMapping原型
HANDLE CreateFileMapping (
HANDLE hFile,      // 映射文件的句柄,若设为0xFFFFFFFF则创建一个进程间共享的对象
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,    // 安全属性DWORD flProtect,     // 保护方式
DWORD dwMaximumSizeHigh,    //对象的大小
DWORD dwMaximumSizeLow,   
LPCTSTR lpName     // 映射文件名,即共享内存的名称 );
 
与虚拟内存类似,保护方式参数可以是PAGE_READONLY或是PAGE_READWRITE。如果多进程都对同一共享内存进行写访问,则 必须保持相互间同步。映射文件还可以指定PAGE_WRITECOPY标志,可以保证其原始数据不会遭到破坏,同时允许其他进程在必要时自由的操作数据的 拷贝。
例如:创建一个名为“zzj”的长度为4096字节的有名映射文件:
HANDLE m_hMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF),
NULL,PAGE_READWRITE,0,0x1000," zzj");
2、在创建文件映射对象后,服务器端进程调用MapViewOfFile函数映射到本进程的地址空间内;
例:映射缓存区视图
void* m_pBaseMapFile=MapViewOfFile(m_hMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,
0,0,0);
3、客户端进程访问共享内存对象,需要通过内存对象名调用OpenFileMapping函数,以获得共享内存对象的句柄
HANDLE m_hMapFile =OpenFileMapping(FILE_MAP_WRITE,
FALSE," zzj");
4、如果客户端进程获得共享内存对象的句柄成功,则调用MapViewOfFile函数来映射对象视图。用户可以使用该对象视图来进行数据读写操作,以达到数据通讯的目的。
例:映射缓存区视图
void* m_pBaseMapFile=MapViewOfFile(m_hMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,
0,0,0);
5、当用户进程结束使用共享内存后,调用UnmapViewOfFile函数以取消其地址空间内的视图:
if (m_pBaseMapFile)
{
UnmapViewOfFile(m_pBaseMapFile);
SharedMapView=NULL;
}
三、利用消息实现内部进程通讯
1、Windows消息机制简介
Windows是一种面向对象的体系结构,Windows环境和应用程序都是通过消息 来交互的。Windows应用程序开始执行后,Windows为该程序创建一个"消息队列(message queue)",用以存放邮寄给该程序可能创建的各种不同窗口的消息。消息队列中消息的结构(MSG)为:
typedef struct tagMSG{
       HWND hwnd;
       UINT message;
       WPARAM wParam;
       LPARAM lParam;
       DWORD time;
       POINT pt;
       }MSG;
其中第一个成员变量是用以标识接收消息的窗口的窗口句柄;第二个参数便是消息标识号,如WM_PAINT;第三个和第四个参数的具体意义和 message值有关,均为消息参数。前四个参数是非常重要和经常用到的,至于后两个参数则分别表示邮寄消息的时间和光标位置(屏幕坐标)。
把消息传送到应用程序有两种方法:一种是由系统将消息"邮寄(post)"到应用程序 的"消息队列",这是"进队消息",Win32 API有对应的函数:PostMessage(),此函数不等待该消息处理完就返回;而另一种则是由系统在直接调用窗口函数时将消息"发送(send)" 给应用程序的窗口函数,属于"不进队消息",对应的函数是SendMessage(),该函数必须等待消息处理完后方可返回。
2、同机进程利用Windows自定义消息进行通信的方法:
     在发送方程序和接收方程序中均定义Windows自定义消息:
例如:#define WM_SERVERDATACHANGE  WM_USER+999
     获取接收方的接收消息的窗口对象
(1)通过接收方程序的窗口标题获取接收方程序的窗口对象,调用函数为:
   staticCWnd*PASCALFindWindow(
LPCTSTRlpszClassName,//窗口类名称,可为NULL
LPCTSTRlpszWindowName); //窗口标题
   例如:接收方程序的窗口标题为“WFClient”
      CString str="WFClient";
CWnd *pWnd=CWnd::FindWindow(NULL,str);
   (2)通过窗口句柄获取接收方程序的窗口对象,调用函数为:
staticCWnd*PASCALFromHandle(HWNDhWnd);
     获取接收方程序的窗口对象后,调用窗口类的SendMessage()或PostMessage()函数
   发送消息
BOOL PostMessage( UINTmessage, WPARAMwParam = 0, LPARAM lParam = 0 );
LRESULTSendMessage(UINTmessage,WPARAMwParam=0,LPARAMlParam=0);
例:if(pWnd)
              pWnd->PostMessage(WM_ SERVERDATACHANGE ,0,0);
     在接收方程序中响应自定义消息,须放在主框架类中,否则不响应
   void CMainFrame::OnData2Chane(WPARAM wParam, LPARAM lParam)
{
           AfxMessageBox("receive WM_USER+999");
}
四、实例程序
1、设计概述
◆ 本实例采用WINDOWS系统共享内存方式,在同一结点上运行的服务器端程序与客户端程序进行数据交换,共享内存的名称为“zzj”
◆ 共享内存分为不同的数据区,服务器端程序与客户端程序刷新各自的数据区,同时访问对方的数据区以获得实时的数据,数据区定义如下:
(1)    数据区1(客户端修改,服务器端读取):
   数据区长度为 4*(n+1) bytes, n 为数据个数(每个数据对应一个点),其结构如下所示:
数据个数+1 (INT ,4BYTES)
数据1   (INT ,4BYTES)
数据2      (INT ,4BYTES)
、、、、、
    数据N      (INT ,4BYTES)
(2)    数据区2(服务端修改,客户端读取):
   数据区长度为 4*(n+1) bytes, n 为数据个数(每个数据对应一个点),其结构如下所示:
        数据个数    (INT ,4BYTES)
        数据1      (INT ,4BYTES)
        数据2      (INT ,4BYTES)
        、、、、、
        数据N    (INT ,4BYTES)
(3)    窗口句柄区
   窗口句柄区长度为8 bytes,服务器端和客户端使用对方的窗口句柄传递WINDOWS用户自定义消息,其结构如下图所示:
     服务器端窗口句柄(4bytes)
     客户端窗口句柄 (4bytes)
◆ 由于对共享内存数据访问存在并发性,服务器端程序与客户端程序使用对方的窗口句柄传递WINDOWS用户自定义消息来实现相互数据转送的驱动。一般情况 下,当服务器端程序的数据区2发生变化时应主动向客户端程序发消息;客户端更改数据区1后,也应主动向服务端发送消息,通知服务端读取。
◆ 用户自定义WINDOWS消息
(1)、服务端à客户端
#define WM_SERVERDATACHANGE  WM_USER+999
当服务端发现数据区2发生变化时,发出此消息
(2)、客户端à服务端
#define WM_CLIENTDATACNANGE WM_USER+998
当客户端发现数据区1发生变化时,发出此消息
2、实现过程
(1)      利用VC++6.0 的AppWizard分别创建二个单文档SDI应用程序,项目名分别为“WFServer”、“WFClient”,视图类的基类采用CFormView。
(2)      为 了便于扩展与应用,笔者在两个进程中封装了对应的两个类CServerData和CClientData 你可以根据自己的共享内存区协议,在这两个类的基础上添加新的功能。CServerData类是服务器端的封装类,主要功能:创建共享内存区,建立映射缓 存区视图,初始化共享内存区的各数据区指针,读、写相应数据区的数据,类的定义如下:

免责声明:文章转载自《Windows下进程通信方式[转]》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇C++随机崩溃捕捉处理【源码分析】FastJson全局配置日期格式导致@JSONField(format = "yyyy-MM-dd")注解失效下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

详解如何实现在线聊天系统中的实时消息获取

序言 传统web浏览器应用采用客户端主动请求方式,只有在收到浏览器请求时服务端才返回消息,这种模式已经不能满足日益多样化的web应用需求,例如: 在线聊天系统:需要实时获取聊天消息。 实时监控系统:需要实时获取监控对象状态。如仪表读数、告警信息等。 随着html技术演进,发展出了多种服务器推送技术,用于服务器向浏览器客户端推送消息。 Ajax轮询 采用Aj...

Windows更新导致的打印问题

最近很多BarTender用户都出现了打印问题,如3700错误消息及3721错误消息。这个问题存在于BarTender的所有版本,这主要是因为什么?又该如何去解决呢? 根据BarTender英文官网提示,这主要是因为用户应用了Microsoft Windows安全更新,应用了该安全更新之后,连续打印多个文档时,前两个文档也许可以成功打印,但第三个以及后面的...

frameset框架弹出层

     前段时间做项目,有个功能是消息提醒。 我相信很多大牛都做过。下面来分享我遇到的问题和解决方案。      首先我们的项目是用frameset框架,main代码。 <frameset name="myFrame" cols="85,*" frameborder="no" border="0" framespacing="0">...

Linux内存管理原理

本文以32位机器为准,串讲一些内存管理的知识点。 1. 虚拟地址、物理地址、逻辑地址、线性地址 虚拟地址又叫线性地址。linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻辑地址专指下文说的线性偏移前的地址)是一个概念。物理地址自不必提。内核的虚拟地址和物理地址,大部分只差一个线性偏移量。用户空间的虚拟地址和物理地址则采用了多级...

一个消息调度框架构建

基本框架 MDU(消息分发单元):包含一个消息处理任务,包含自身的消息队列,是一个消息调度的基本单位。 PID (功能子模块) :框架中用PID作为模块的划分,每个模块具有自己的PID编号,根据功能和调度需求可以安排多个PID到一个MDU中,PID是消息通信的一个基本单位,每个PID提供一个消息处理入口。 MQ (消息队列) :使用消息队列作为任务通信...

换Ubuntu邮件客户端Evolution为Thunderbird

ubuntu 10.04右上角的这个消息通知的小控件还是蛮喜欢的,不过到今天才知道它叫indicator applet。这里自带的邮件客户端是Evolution,配置好以后,收取邮件的过程相当的慢,而且还总失去响应,实在是难用。 换成thunderbird的原因如下: 用久了,已经习惯了。 设置简单,对Gmail设置的时候可以自动配置。 响应比Evolut...