进程通信Word文档下载推荐.docx
《进程通信Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《进程通信Word文档下载推荐.docx(28页珍藏版)》请在冰豆网上搜索。
同时给出了发送数据程序和接收数据程序的主要源代码。
图3.1和图3.2是本章开发的进程之间通信程序的演示对话框。
进程间通信是一个非常复杂的过程,涉及的问题也较多,例如进程互斥和同步等,这些问题在本书中不做介绍。
发送数据程序对话框
接收数据程序对话框
3.2
在学习编写进程之间通信的程序之前,有必要先介绍一些有关的背景知识,这样有助于读者对一些有关进程间通信方面的概念有较深入的了解。
3.2.1
Windows进程间标准通信技术的发展
我们既可以用非标准的进程间通信技术,如Windows消息、内存映射和内存共享等,也可以用标准的通信技术。
微软标准进程间通信技术的发展过程如下所述。
(1)进程间通信初期
自从有Windows操作系统后,剪贴板(Clipboard)首先解决了不同程序间的通信问题(由剪贴板作为数据交换中心,进行复制、粘贴的操作)。
但是剪贴板传递的都是“死”数据,应用程序开发者得自行编写、解析数据格式的代码。
于是动态数据交换(DynamicDataExchange,DDE)的通信协定应运而生,它可以让应用程序之间自动获取彼此的最新数据。
但是,解决彼此之间的数据格式转换仍然是程序员沉重的负担。
对象的链接和嵌入(ObjectLinkingandEmbedded,OLE)的诞生把原来应用程序的数据交换提高到“对象交换”,这样程序间不但获得数据,而且也可以获得彼此的对象,并且可以直接使用彼此数据内容。
(2)OLE(对象链接与嵌入)
1991年制定的OLE1.0规范主要解决多个应用程序之间的通信和消息传递问题,微软希望第三方开发商能够遵守这个规范,以便在当时的Windows平台上的应用程序能够相互协调工作,更大地提高工作效率。
然而事与愿违,只有很少的软件开发商支持它。
为此,微软于1993年发布了新的规范——OLE2.0,它在原有的基础上完善并增强了以下各方面的性能:
①OLE自动化,一个程序有计划地控制另一个程序的能力;
②OLE控件,小型的组件程序,可嵌入到另外的程序,提供自己的专有功能;
③OLE文档,完善了早期的混合文档功能,不仅支持简单的链接和嵌入,还支持在位激活、拖放等功能。
(3)ActiveX战略
同OLE1.0相比,OLE2.0得到了很多软件厂商的支持。
许多程序设计人员编写了大量的实现OLE自动化服务器功能的组件(不一定是EXE文件),这些组件一般不求功能齐全、强大,而是可以实现专门的功能,被其他程序编程控制,由此承袭OLE的名字,称为OLE控件。
它们在文件名中的扩展名一般为ocx(OLEControlExtension)。
微软刚刚赢得广大软件厂商的支持,使OLE技术深入人心,然而天不如人愿,国际互联网的超速发展让比尔·
盖茨始料未及。
加上早期的OLE1.0不得人心,导致后来的人们总把在Word中插入一个图形当作OLE技术的全部,各类资料在介绍新OLE技术时命名也不统一,造成很大的混乱。
针对这些情况,微软在1996年重新制订了一个关于OLE的规
范——OLE96。
这个规范扩展了OLE控件的能力,并贯彻微软的Internet战略使它更易于在网络环境中使用,还考虑命名混淆的问题,重新给OLE控件贴上一个标签——ActiveX控件。
不仅如此,以前的什么OLE文档也相应称为ActiveX文档了。
总之,为了满足Internet战略,微软把OLE换成了ActiveX,企图使人们重新看待新的OLE——ActiveX,把它看成网络上的解决软件组件问题的标准。
(4)OLE/ActiveX与COM/DCOM比较
OLE/ActiveX名称比COM/DCOM更为我们熟悉,其实OLE和ActiveX是商业名称,它们的发展过程为OLE→ActiveX(网络OLE)。
COM和DCOM是纯技术名词,它们是OLE/ActiveX基础,其发展过程为COM→DCOM(网络COM),其中COM(ComponentObjectModel,组件对象模式)是在OLE2.0中建立的规范。
OLE/ActiveX不仅可以实现进程之间的通信,而且可以创建进程,它们是“类厂”组件对象。
3.2.2
应用程序与进程
应用程序和进程在概念上是有一定区别的,前者是静态的程序代码,而后者是动态的实体。
只有应用程序加载到系统中后才能成为一个进程。
Windows进程分为独立进程和共享进程两种。
一般情况下,人们把独立运行的程序称为进程,其实这只是独立进程。
在实际情况下也常常遇到另一种情况,即一个应用程序可能启动多个进程,一个进程空间可以运行多个程序,这就是共享进程。
例如,同一个应用程序重复运行就启动了多个进程;
而在一个进程中调用其他程序,或者通过程序挂钩,这就使同一个进程空间里运行了多个程序。
这里着重讨论独立进程之间的通信。
3.2.3
进程之间通信的类型
根据不同的标准,进程之间通信类型有不同的划分方法。
实际中也有多种划分方法,这里只给出几种划分方法如下所述。
(1)低级通信和高级通信
·
低级通信:
只能传递状态和整数值(控制信息),包括进程互斥和同步所采用的信号量和管程机制。
其第一个缺点为传送信息量小、效率低,每次通信传递的信息量固定,若传递较多信息则需要进行多次通信;
第二个缺点是编程复杂,直接实现通信的细节,容易出错。
高级通信:
能够传送任意数量的数据,包括共享存储区、管道、消息等。
(2)直接通信和间接通信
l
直接通信:
信息直接传递给接收方,如管道,在发送时,指定接收方的地址或标识,也可以指定多个接收方或广播式地址;
在接收时,允许接收来自任意发送方的消息,并在读出消息的同时获取发送方的地址。
间接通信:
借助于收发双方进程之外的共享数据结构作为通信中转,如剪贴板。
通常接收和发送方的数目可以是任意的。
(3)本地通信和远程通信
本地通信方式:
这种通信又称之为同机通信,它是在同一台计算机上的程序之间进行的,也就是说客户进程和服务进程位于同一台计算机上。
远程通信方式:
这种通信又称之为网间的进程通信,要解决的是不同主机进程间的相互通信问题(可把同机进程通信看作是其中的特例)。
在这种通信中,首先要解决的是网络间的进程标识问题。
同一主机上,不同进程可用进程号(processID)唯一标识。
但在网络环境下,各主机独立分配的进程号不能唯一标识该进程。
例如,主机A赋予某个进程号5,在B机中也可以存在5号进程,因此,“5号进程”这句话就没有意义了。
其次,操作系统支持的网络协议众多,不同协议的工作方式不同,地址格式也不同。
因此,网间的进程通信还要解决多重协议的识别问题。
3.3
使用自定义消息通信
Windows程序与其他类型程序的区别就是使用消息,例如键盘或鼠标消息等,在DOS系统下的程序没有定义消息。
在Windows操作系统中,消息不但可以用于进程内的通信,也可以用于进程间的通信。
这里重点介绍进程间的消息通信。
3.3.1
通过自定义消息实现进程间通信的方法
消息分为两种,即系统消息和用户(程序设计者)自定义消息。
系统消息定义从0到0x3FF,可以使用0x400到0x7FFF定义自己的消息。
Windows把0x400定义为WM_USER。
如果想定义自己的一个消息,可以在WM_USER上加上一个值。
还有一种自定义窗口消息的方法是用RegisterWindowsMessage()函数来注册这个消息。
与在WM_USER上加上某个数相比,它的好处是不必考虑所表示的消息标识符是否超出工程的允许范围。
要想用消息实现进程间通信,则需要在这两个程序中定义或注册相同的消息,才能保证数据通信顺利进行。
用户自定义消息的方法如下:
#defineWM_COMM
WM_USER+100
constUINTwm_nRegMsg=RegisterWindowMessage("
reg_data"
);
有了这两种定义的消息后,可以用如下的方法来发送消息。
pWnd->
SendMessage(WM_COMM,NULL,(LPARAM)uMsg);
SendMessage(wm_nRegMsg,NULL,(LPARAM)uMsg);
其中pWnd为接收这个消息的窗口句柄,uMsg为要通过消息发送的数据,是长整型。
这两个消息的发送可以分别在一个发送函数里实现,其具体的做法见以后的实例。
通过实验可知,这两种自定义消息的发送效果是一样的。
在接收消息的程序中,除与发送消息的程序需要定义相同的消息外,还需要定义相应的消息映射和消息映射函数,消息的映射方法如下:
ON_MESSAGE(WM_COMM,OnUserReceiveMsg)
ON_REGISTERED_MESSAGE(wm_nRegMsg,OnRegReceiveMsg)
与以上消息映射对应的函数定义如下:
voidCDataRecvDlg:
:
OnUserReceiveMsg(WPARAMwParam,LPARAMlParam)
{
//增加用户自定义程序代码
…
}
//--------------------------------------------------------------------
OnRegReceiveMsg(WPARAMwParam,LPARAMlParam)
//增加用户自定义程序代码
其中OnUserReceiveMsg()函数为WM_COMM消息的映射函数,OnRegReceiveMsg()函数为wm_nRegMsg消息的映射函数。
可以看出,这两种消息的映射函数形式是一样的。
3.3.2
通过自定义消息实现进程间通信的实例
为说明以自定义消息实现进程之间的通信,作者用VC++编写了这样的程序。
有两个对话框程序,其中一个为发送程序,另一个为接收程序。
在这两个程序中分别定义了两个消息WM_COMM和wm_nRegMsg,在CDataSendDlg类中增加了用于发送数据的两个函数,即voidCDataSendDlg:
OnSendUsermsg()和voidCDataSendDlg:
OnSendRegmsg()。
它们的源代码如下:
voidCDataSendDlg:
OnSendUsermsg()
UpdateData();
//更新数据
CWnd*pWnd=CWnd:
FindWindow(NULL,_T("
DataRecv"
));
//查找DataRecv进程
if(pWnd==NULL){
AfxMessageBox(TEXT("
UnabletofindDataRecv."
return;
}
UINTuMsg;
uMsg=atoi(m_strUserMsg);
pWnd->
//发送.
OnSendRegmsg()
AfxMessageBox("
uMsg=atoi(m_strRegMsg);
//发送
在接收数据的程序中要做三件事:
①定义自定义消息;
②定义消息映射表;
③定义消息映射函数。
自定义消息的方法如前面所述。
在CDataRecvDlg类中增加了两个用于接收数据的函数,即voidCDataRecvDlg:
OnUserReceiveMsg()和voidCDataRecvDlg:
OnRegReceiveMsg()。
消息映射表如下:
BEGIN_MESSAGE_MAP(CDataRecvDlg,CDialog)
//{{AFX_MSG_MAP(CDataRecvDlg)
ON_MESSAGE(WM_COMM,OnUserReceiveMsg)
ON_REGISTERED_MESSAGE(wm_nRegMsg,OnRegReceiveMsg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
消息映射表中的映射函数名的格式是一样的。
其实它们可以用同一个函数,为了说明方便,这里把它们的名字取为不一样。
接收数据程序中的消息映射函数的源代码如下:
m_strUserMsg.Format("
%d\n"
int(lParam));
UpdateData(FALSE);
m_strRegMsg.Format("
从上面的实例中可以看出,以自定义消息来进行进程之间的通信存在一定的局限性,即所发送的数据只能是长整型,而对于字符串,则不能进行通信。
要进行字符串或大批量的数据的传输,则需要采用其他的通信方法。
3.4
使用WM_COPYDATA消息通信
对于少量数据可以用WM_COPYDATA方便地实现通信。
由于SendMessage()是阻塞的,只有接收方响应了消息,SendMessage()才能返回,否则一直阻塞。
所以,对于大量数据来说,用SendMessage()就容易造成窗口假死。
3.4.1
通过WM_COPYDATA消息实现进程间通信的方法
在Win32中,WM_COPYDATA消息主要目的是允许在进程间传递只读数据。
SDK文档推荐用户使用SendMessage()函数,接收方在数据复制完成前不返回,这样发送方就不可能删除和修改数据。
这个函数的原型如下:
SendMessage(WM_COPYDATA,wParam,lParam)
其中wParam设置为包含数据的窗口句柄,lParam指向一个COPYDATASTRUCT的结构,其定义为:
typedefstructtagCOPYDATASTRUCT{
DWORDdwData;
DWORDcbData;
PVOIDlpData;
}COPYDATASTRUCT;
其中dwData为自定义数据,cbData为数据大小,lpData为指向数据的指针。
需要注意的是,WM_COPYDATA消息保证发送的数据从原进程复制到目标进程。
但是,WM_COPYDATA消息不能发送HDC、HBITMAP之类的东西,它们对于目标进程来说是无效的。
目标进程得到这些数据不能在原进程作任何事情,因为它们属于不同的进程。
与其他进程通信方法一样,要实现进程间的数据通信,在发送数据的程序中,首先要找到接收数据进程的窗口句柄pWnd,可以用CWnd:
FindWindow(NULL,_T("
))函数来得到,其中字符串"
为接收数据的程序名。
然后用SendMessage()函数发送数据,其具体的做法见后面的实例。
在接收数据的程序中,首先在消息映射表中增加WM_COPYDATA消息映射,然后定义消息映射函数,其函数的格式为:
BOOLCDataRecvDlg:
OnCopyData(CWnd*pWnd,COPYDATASTRUCT*pCopyDataStruct)
3.4.2
通过WM_COPYDATA消息实现进程间通信的实例
与前面所说的自定义消息不一样,WM_COPYDATA消息是Win32提供的消息。
与自定义消息相比较,WM_COPYDATA消息可以传递一个较大的数据块。
这里仍然用两个对话框程序来实现WM_COPYDATA消息的通信。
以下分别给出发送数据程序的发送函数和接收数据程序的接收函数。
在发送数据的对话框类CDataSendDlg中,用MFCClassWizard工具或者手工的方法增加函数voidCDataSendDlg:
OnSendCopydata(),其具体代码如下:
OnSendCopydata()
COPYDATASTRUCTcpd;
//给COPYDATASTRUCT结构赋值
cpd.dwData=0;
cpd.cbData=m_strCopyData.GetLength();
cpd.lpData=(void*)m_strCopyData.GetBuffer(cpd.cbData);
SendMessage(WM_COPYDATA,NULL,(LPARAM)&
cpd);
在用MFCAppWizard(exe)创建接收数据的对话框程序后,生成对话框类CDataRecvDlg。
在这个类中,首先要定义接收WM_COPYDATA消息的映射,可以用ClassWizard工具来增加,也可以手动增加,但手动增加需要修改三个地方:
①在消息映射表中增加ON_WM_COPYDATA();
②增加成员函数BOOLCDataRecvDlg:
OnCopyData();
③在CDataRecvDlg类中增加WM_COPYDATA消息映射函数的定义。
WM_COPYDATA消息的映射如下:
ON_WM_COPYDATA()
CDataRecvDlg:
OnCopyData()函数的定义如下:
m_strCopyData=(LPSTR)pCopyDataStruct->
lpData;
//获得实际长度的字符串
m_strCopyData=m_strCopyData.Left(pCopyDataStruct->
cbData);
returnCDialog:
OnCopyData(pWnd,pCopyDataStruct);
其中m_strCopyData为接收到的字符串,pCopyDataStruct为COPYDATASTRUCT结构指针。
注意由pCopyDataStruct直接得到的m_strCopyData字符串长度可能不是实际发送的字符串长度,需要用发送字符串时所给定的字符串长度来进一步确定,其长度由pCopyDataStruct->
cbData来得到。
3.5
对于ReadProcessMemory()和WriteProcessMemory()函数的通信方法,在第1章已做介绍。
并用它说明了C指针的意义,但有两点需要改进:
①接收程序在接收数据时所用的指针代码值不需要事先给定;
②内存大小是可以变化的。
这里将对内存读写函数的通信方法做一点改进。
3.5.1
使用内存映射文件通信的方法
采用内存映射(FileMapping)机制可以将整个文件映射为进程虚拟地址空间的一部分来