




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、3.4使用 WM_COPYDATA 消息通信对于少量数据可以用 WM_COPYDAT 便地实现通信。 由于 SendMessage()是阻塞的,只有接收方响应了消息,SendMessage()才能返回,否则一直阻塞。所以,对于大量数据来说,用 SendMessage()就容易造成窗口假死。3.4.1通过 WM_COPYDATA 消息实现进程间通信的方法在 Win32 中,WM_COPYDATA 主要目的是允许在进程间传递只读数据。SDK 文档推荐用户使用 SendMessage()函数,接收方在数据复制完成前不返回,这样发送方就不可能删除和修改数据。这个函数的原型如下:SendMessage(
2、WM_COPYDATA,wParam,lParam)其中 wParam 设置为包含数据的窗口句柄,lParam 指向一个 COPYDATASTRUCT 结构,其定义为:typedefstructtagCOPYDATASTRUCTDWORDdwData;DWORDcbData;PVOIDlpData;COPYDATASTRUCT;其中 dwData 为自定义数据,cbData 为数据大小,lpData 为指向数据的指针。需要注意的是,WM_COPYDATA、保证发送的数据从原进程复制到目标进程。但是,WM_COPYDATA 不能发送 HDCHBITMA 此类的东西,它们对于目标进程来说是无效的。
3、目标进程得到这些数据不能在原进程作任何事情,因为它们属于不同的进程。与其他进程通信方法一样,要实现进程间的数据通信,在发送数据的程序中,首先要找到接收数据进程的窗口句柄 pWnd 可以用 CWnd:FindWindow(NULL,_T(DataRecv)函数来得到,其中字符串DataRecv”为接收数据的程序名。然后用 SendMessage()函数发送数据,其具体的做法见后面的实例。在接收数据的程序中, 首先在消息映射表中增加 WM_COPYDAT 愈映射, 然后定义消息映射函数,其函数的格式为:BOOLCDataRecvDlg:OnCopyData(CWnd*pWnd,COPYDATAST
4、RUCT*pCopyDataStruct)/增加用户自定义程序代码3.4.2通过 WM_COPYDATA 消息实现进程间通信的实例与前面所说的自定义消息不一样,WM_COPYDAT 愈是 Win32 提供的消息。与自定义消息相比较,WM_COPYDAT 愈可以传递一个较大的数据块。这里仍然用两个对话框程序来实现 WM_COPYDAT 愈的通信。以下分别给出发送数据程序工勺发送函数和接收数据程序的接收函数。在发送数据的对话框类 CDataSendDlg 中, 用 MFCClassWizard 工具或者手工的方法增力口函数voidCDataSendDlg 二 OnSendCopydata(),其具
5、体代码如下:voidCDataSendDlg:OnSendCopydata()UpdateData();/更新数据CWnd*pWnd=CWnd:FindWindow(NULL,_T(DataRecv);/查找 DataRecv 进程if(pWnd=NULL)AfxMessageBox(UnabletofindDataRecv.);return;COPYDATASTRUCTcpd;/给 COPYDATASTRUCT 结构赋值cpd.dwData=0;cpd.cbData=m_strCopyData.GetLength();cpd.lpData=(void*)m_strCopyData.GetBu
6、ffer(cpd.cbData);pWnd-SendMessage(WM_COPYDATA,NULL,(LPARAM)&cpd);/发送在用 MFCAppWizard(exe)创建接收数据的对话框程序后,生成对话框类CDataRecvDlg。在这个类中,首先要定义接收 WM_COPYDATA 的映射,可以用ClassWizard 工具来增加,也可以手动增加,但手动增加需要修改三个地方:在消息映射表中增加 ON_WM_COPYDATA()增力口成员函数BOOLCDataRecvDlg:OnCopyData();在 CDataRecvDlg 类中增加 WM_COPYDATA 映射函数的定义
7、。WM_COPYDATA 的映射如下:BEGIN_MESSAGE_MAP(CDataRecvDlg,CDialog)/AFX_MSG_MAP(CDataRecvDlg)ON_WM_COPYDATA()/AFX_MSG_MAPEND_MESSAGE_MAP()CDataRecvDlg:OnCopyData()函数的定义如下:BOOLCDataRecvDlg:OnCopyData(CWnd*pWnd,COPYDATASTRUCTpCopyDataStruct)m_strCopyData=(LPSTR)pCopyDataStruct-lpData;/获得实际长度的字符串m_strCopyData=m
8、_strCopyData.Left(pCopyDataStruct-cbData);/更新数据UpdateData(FALSE);returnCDialog:OnCopyData(pWnd,pCopyDataStruct);)其中 m_strCopyData 为接收至用勺字符串,pCopyDataStruct 为COPYDATASTRUCT 结构指针。注意由 pCopyDataStruct 直接得到的 m_strCopyData 字符串长度可能不是实际发送的字符串长度,需要用发送字符用时所给定的字符串长度来进一步确定,其长度由 pCopyDataStruct-cbData 来得到。3.5使用内
9、存读写函数和内存映射文件通信对于 ReadProcessMemory(并口 WriteProcessMemory()函数的通信方法,在第 1 章已做介绍。并用它说明了 C 指针的意义,但有两点需要改进:接收程序在接收数据时所用的指针代码值不需要事先给定;内存大小是可以变化的。这里将对内存读写函数的通信方法做一点改进。3.5.1使用内存映射文件通信的方法采用内存映射(FileMapping)机制可以将整个文件映射为进程虚拟地址空问的一部分来加以访问。这种方法和实例前面已做了详细介绍,这里不再重复。3.5.2使用内存读写函数实现进程间通信的方法要使接收程序获得发送程序的数据指针,可以通过发送消息方
10、法来进行,即通过消息把数据指针从发送程序传递到接收程序。也可以用第 1 章所介绍的方法:先获得发送程序中的被发送数据指针,然后把这个指针直接赋值给接收数据的程序。但这种方法在实际操作中较困难,使用起来不方便。要使用发送消息的方法来传递指针,就需要定义一个用户消息。可用如下的自定义消息来传递指针,即constUINTwm_nMemMsg=RegisterWindowMessage(mem_data);要通过内存来传递数据,还必须要在内存中申请一定的内存空间,这一点很重要。用变量定义的方法只能申请有限的固定的内存空间,例如,定义一个 char 变量只能在内存里申请到一个字节的内存空间,定义一个 i
11、nt 变量只能在内存里申请到 4 个字节的内存空间。 如果要分配一块内存空间存放数据, 可以调用 GlobalAlloc()或者 VirtualAllocEx()等来实现。3.5.3使用内存读写函数实现进程间通信的实例自定义消息和内存读写函数(ReadProcessMemory()和 WriteProcessMemory()相结合,利用它们各自的长处进行通信。自定义消息通信只能传递一个长整型数值,而内存读写函数却需要一个内存读写地址,并且缺少一个传递数据指针的方法。这样它们正好可以“合作”,来进行大批量的数据传递工作。要进行这种方式的通信,同样需要编写两个对话框程序,并且在这两个程序中分别定义
12、一个相同的用于传递指针的消息 wm_nMemMs 讴里借用前面所使用的发送数据对话框类 CDataSendDlg 和接收数据才话框类 CDataRecvDlg。在CDataSendDlg 中,用 MFCClassWizard 工具或手动增加成员函数voidCDataSendDlg:OnSendMem(),其源代码如下:voidCDataSendDlg:OnSendMem()UpdateData();/更新数据CWnd*pWnd=CWnd:FindWindow(NULL,_T(DataRecv);/查找 DataRecv 进程if(pWnd=NULL)AfxMessageBox(Unableto
13、findDataRecv.);return;/获取进程号DWORDPID;GetWindowThreadProcessId(pWnd-m_hWnd,(DWORD*)&PID);HANDLEhProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,PID);/分配虚拟内存LPVOIDlpBaseAddress;lpBaseAddress=VirtualAllocEx(hProcess,0,BUFFER_SIZE,MEM_COMMIT,PAGE_READWRITE);chardataBUFFER_SIZE;strcpy(data,m_strMem);/把字
14、符串写入 hProcess 进程的内存WriteProcessMemory(hProcess,lpBaseAddress,data,BUFFER_SIZE,NULL);/发送基址给 DataRecv 进程pWnd-SendMessage(wm_nMemMsg,NULL,(LPARAM)lpBaseAddress);/等待接收程序接收数据Sleep(100);/释放虚拟内存VirtualFreeEx(hProcess,lpBaseAddress,0,MEM_RELEASE);)从以上程序中可以看出如何使用 WriteProcessMemory()和 wm_nMemM 的息来发送字符串 m_str
15、Mem 这段程序中,首先,寻找接收数据的程序 DataRecv 的窗口指针 pWncff 口进程句柄 hProcess,再用 VirtualAllocEx()函数在这个进程中申请虚拟内存空间。然后,用 WriteProcessMemory()把字符串 m_strMem 存放入虚拟内存,并且通过消息 wm_nMemM 题所申请的存空间起始地址发送给数据接收程序。 最后,当数据接收程序接收到数据后,用 VirtualFreeEx()释放所中请的虚拟内存。在数据接收程序的对话框类 CDataRecvDlg 中,需要定义 wm_nMemM 的息映射,它在消息映射表中的表示方法如下:BEGIN_MESS
16、AGE_MAP(CDataRecvDlg,CDialog)/AFX_MSG_MAP(CDataRecvDlg)ON_REGISTERED_MESSAGE(wm_nMemMsg,OnRegMemMsg)/AFX_MSG_MAPEND_MESSAGE_MAP()在数据接收对话框类 CDataRecvDlg 中, 用 MFCClassWizard 工具或手动增力口消息映射函数 voidCDataRecvDlg:OnRegMemMsg(),其定义如下:voidCDataRecvDlg:OnRegMemMsg(WPARAMwParam,LPARAMlParam)LPVOIDlpBaseAddress=(
17、LPVOID)lParam;/把字符串写入 hProcess 进程的内存HANDLEhProcess=GetCurrentProcess();chardataBUFFER_SIZE;ReadProcessMemory(hProcess,lpBaseAddress,data,BUFFER_SIZE,NULL);m_strMem=data;/更新数据UpdateData(FALSE);3.6使用动态链接库通信动态链接库(DynamicLinkLibrary,DLD 不仅可以用来共享程序代码,而且可以用来共享数据。用 DLL 共享程序代码的方法以后再做介绍,这里只说明如何利用它共享数据。3.6.1D
18、LL 概述目前,动态链接库有 Win32DLL 和 Win16DLL 之分,它们的特性不相同。但是,对于同一个 DLL,不管 Win32DLL 还是 Win16DLL 的代码装入内存实际只有一次。这里必须强调的是对同一个 DLL 的调用才是这样。如果同一个 DLL 有多个副本,且每个 DLL 副本分别被不同的程序调用时,那么每个副本都会被装入内存一次。装入内存后,虽然说它们名字相同,但它们是多份副本,因此不是对同一个 DLL 的调用。在这种情况下就不能用这个DLL 实现进程之间的通信。例如在 c:DLL_A 目录下有动态库文件 shared.dll 和应用程序user1.exe, 而在另外一个
19、 c:DLL_B 目录下有动态库文件 shared.dll 和应用程序 user2.exe,那么就不能通过 shared.dll 来实现 user1.exe 和 user2.exe 之间的通信。Win32把 DLL装入全局内存并把 DLL映射到每个程序的地址空间, 并且不需要把 DLL映射到每个进程相同的地址空间上。 DLL 成为载入它的进程的一部分, 而不像在 Win16 中,成为系统的一部分。对于 Win16 来说,在 DLL 中共享数据是很容易的,因为访问 DLL 的每个应用程序都可以得到它的全局静态变量,如图 3.3 所示。在 Win32 中,对每个载入 DLL 的进程,DLL获取一个
20、该进程的唯一的全局静态变量的副本,如图 3.4 所示。3.4 的全局揄 S 襄里蜜 3-3Winli 的全局部态襄虽的所有的应用也序共享的所有的应用程序共享3.6.2使用 DLL 通信的方法卜二二一二二二二二 1 二一二二二二二二二二二 1二一二从上面可以看出,对于 Win32 的 DLL 所有载入 DLL 的应用程序只能共享程序代码,不能共享数据,必须要采取一种方法才能使这些程序之间共享数据。当然访问数据时要注意同步问题。要想使 Win32DLL 的数据区能设计成共享的存储区,可以通过#pragmadata_seg 指令建立一个新段来做到这一点,实际上是告诉编译器包含段中的特定变量。然而,仅
21、此不足以做到数据共享,还必须把段中将要共享的变量告诉连接器。可通过如下命令来实现:(1)指定 DEFi 件在 SECTION 毂下的名字,如下所示:SECTIONS共享段名 READWRITESHAREDNew#pragmacomment(linker,/SECTION:共享段名,RWS)下面的例子示范了如何初始化一个全局变量:#pragmadata_seg(MyShared)UINTm_glnData=0;#pragmadata_seg()#pragmacomment(linker,/SECTION:MyShared,RWS)注意要初始化变量。 初始化变量很重要, 因为编译器将把所有未初始化
22、数据存放在.bss段。把变量安排在与用户预期不同的段中,则它们就不能被共享,除非明确地指出要共享的是.bss 段。关于共享数据段名称 MyShared,完全可以用其他的名称,如 MYDATA.MYSC 等,但建议不要与 PE 文件的固定的段名相同,以免程序运行时出错。最后一点要强调的是,进行通信的程序要使用同一个 DLL 文件。如果使用的是相同 DLL 文件的不同副本,则不能实现进程之间的通信。3.6.3使用 DLL 通信的实例与前面所讲的通信方法不同,本实例使用 DLL 实现进程间的通信。以下是一个用于生成 DLL 文件的头文件 DllObj.h。其具体代码如下:/DllObj.h:头文件/
23、#ifndef_DLLOBJ_H_INCLUDED#define_DLLOBJ_H_INCLUDED#include#ifdef_cplusplus#define_DLLCOM_externC_declspec(dllexport)#else#define_DLLCOMdeclspec(dllexport)#endif_DLLCOM_LPSTRGetValueString();_DLLCOM_voidSetValueString(LPCSTRstr);#endif其中SetValueString()和GetValueString()函数分别用于向所指定的共享存储区里写入和读取字符串。_DLLC
24、OMJ1 于定义 DLL 中函数的输出。可以看出,SetValueString()和GetValueString()函数的写法与其他 DLL 文件的写法没有什么不同。但是,这两个函数所用到的共用字符串变量 m_strString 的表示方法则是用 DLL 实现内存数据共享的关键。现在来看看如何制作一个这样的 DLL 文件。先用 MFCAppWizard(dll)生成一个 dllcom 模板(可以取其他的名称),然后把以下的代码包含在一个 dllcom.cpp 文件中,再用 VC+编译器进行编译和连接,就可以生成 dllcom.dll 和 dllcom.lib 文件。生成这个 DLL 文件的核心
25、代码如下:#pragmadata_seg(MyShared)charm_strString256=TEXT();volatileboolbInCriticalSection=FALSE;#pragmadata_seg()#pragmacomment(linker,/SECTION:MyShared,RWS)CCriticalSectioncs;/从内存中读取字符串_DLLCOM_LPSTRGetValueString()while(bInCriticalSection)/等待Sleep(1);returnm_strString;/把字符串存储到共享内存中_DLLCOM_voidSetValue
26、String(LPCSTRstr)while(bInCriticalSection)/等待Sleep(1);cs.Lock();bInCriticalSection=TRUE;strcpy(m_strString,str);bInCriticalSection=FALSE;cs.Unlock();其中 bInCriticalSection 为进程访问数据时的同步标识。在制作了一个用于进程间通信的 DLL 文件后,就可以利用它实现进程之间的通信。 可以设计两个应用程序, dlluserl和dlluser2,在这两个程序中把动态库文件头DllObj.h和动态库 dllcom.lib 包含其中,即#
27、includeDllObj.h#pragmacomment(lib,dllcom.lib)然后,用 MFCVC+编译器进行编译和连接,这样就可以用 SetValueString()和GetValue-String()函数进行通信了。使用 Windows 剪贴板通信Windows 剪贴板是一种比较简单同时也是开销比较小的 IPC(进程间通信)机制。Windows 系统支持剪贴板 IPC 的基本机制是由系统预留的一块全局共享内存,用来暂存各个进程间进行交换的数据。提供数据的进程创建一个全局内存块,并将要传送的数据移到或复制到该内存块;而接受数据的进程(也可以是提供数据的进程本身)获取此内存块的句柄
28、,并完成对该内存块数据的读取。在 Windows 系统和其他工具软件中有自带的使用剪贴板的命令。例如,在MicrosoftWord 中,组合键 Ctrl+C 用于文字的复制、组合键 Ctrl+X 用于对文字的剪切、组合键 Ctrl+V 用于对文字的粘贴。使用这些命令可以很方便地对所选择字符串进行复制和移动。然而,这里关心的是如何在编写应用程序时使用剪贴板实现进程间的通信。使用剪贴板实现进程间通信的方法可以使用剪贴板函数实现进程间的数据传输。常用的剪贴板函数有:/打开剪贴板BOOLOpenClipboard();/关闭剪贴板BOOLCloseClipboard();/清空剪贴板,并将所有权分配给
29、打开剪贴板的进程BOOLEmptyClipboard();/按指定数据格式放置剪贴板数据,用之前必须使用 OpenClipboard 函数HANDLESetClipboardData(UINTuFormat,HANDLEhMem);/检测是否已经包含了所需要的数据BOOLIsClipboardFormatAvailable(UINTuFormat);/获取指定剪贴板数据HANDLEGetClipboardData(UINTuFormat);其中 uFormat 为剪贴板格式,见 MSDN 微软开发者网络)描述,hMen所申请的内存控制句柄。文本剪贴板和位图剪贴板是比较常用的。 其中, 文本剪贴
30、板是包含具有格式 CF_TEXT勺字符串的剪贴板,是最经常使用的剪贴板之一。在文本剪贴板中传递的反据是不带任何格式信息的 ASCII 字符。若要将文本传送到剪贴板,可以先分配一个可移动全局内存块,然后将要复制的文本内容写入到此内存区域,最后调用剪贴板函数如 OpenClipboard()、SetClipboardData()将数据放置到剪贴板。从剪贴板获取文本的过程与之类似,首先用 OpenClipboard()函数打开剪贴板并获取剪贴板的数据句柄,如果数据存在就复制其数据到程序变量。由于 GetClipboardData()获取的数据句柄属于剪贴板,因此用户程序必须在调用 CloseClip
31、board()函数之前使用它。大多数应用程序对图形数据采取是位图剪贴板数据格式。位图剪贴板的使用与文本剪贴板的使用类似,只是数据格式要指明为 CF_BITMAP 而且在使用 SetClipboardData()或 GetClipboardData()函数时交给剪贴板或从剪贴板返回的是设备相关位图句柄。使用剪贴板实现进程间通信的实例剪贴板中可以存放许多类型的数据,其中包括标准文本格式、位图格式、RTF 格式等,由于类型比较多,这里只给出经常使用的文本格式的实例,其他的数据类型的操作方法基本类似。同样,用 VC+编写两个对话框应用程序。为了方便,仍然借用前面所使用的对话框类 CDataSendDl
32、g 和 CDataRecvDlg。为了把文本放置到剪贴板上,在 CDataSendDlg 中,用 MFCClassWizard 工具或者用手工的方法增加函数 voidCDataSendDlg:OnSendClipboard(),其源代码如下:voidCDataSendDlg:OnSendClipboard()UpdateData();/更新数据CStringstrData=m_strClipBoard;/获得数据/打开系统剪贴板if(!OpenClipboard()return;/使用之前,清空系统剪贴板EmptyClipboard();/分配一内存,大小等于要复制的字符串的大小,返回到内存控
33、制句柄HGLOBALhClipboardData;hClipboardData=GlobalA110c(GMEM_DDESHARE,strData.GetLength()+1);/内存控制句柄加锁,返回值为指向那内存控制句柄所在的特定数据格式的指针char*pchData;pchData=(char*)GlobalLock(hClipboardData);/将本地变量的值赋给全局内存strcpy(pchData,LPCSTR(strData);/给加锁的全局内存控制句柄解锁GlobalUnlock(hClipboardData);/通过全局内存句柄将要复制的数据放到剪贴板上SetClipboa
34、rdData(CF_TEXT,hClipboardData);/使用完后关闭剪贴板CloseClipboard();在数据接收程序的 CDataRecvDlg 类中,用与前面所用的同样的方法,增加从剪贴板上获取文本的函数,即 voidCDataRecvDlg:OnRecvClipboard(),其源代码如下:voidCDataRecvDlg:OnRecvClipboard()/打开系统剪贴板if(!OpenClipboard()return;/判断剪贴板上的数据是否是指定的数据格式if(IsClipboardFormatAvailable(CF_TEXT)|IsClipboardFormatA
35、vaila-ble(CF_OEMTEXT)/从剪贴板上获得数据HANDLEhClipboardData=GetClipboardData(CF_TEXT);/通过给内存句柄加锁,获得指向指定格式数据的指针char*pchData=(char*)GlobalLock(hClipboardData);/本地变量获得数据m_strClipBoard=pchData;/给内存句柄解锁GlobalUnlock(hClipboardData);elseAfxMessageBox(Thereisnotext(ANSI)dataontheClipboard.);/使用完后关闭剪贴板CloseClipboard
36、();/更新数据UpdateData(FALSE);使用剪贴板通信的方法与使用发送消息通信的方法所经历的过程是不一样的,前者是把所共享的数据先放在剪贴板上,然后由接收数据的程序去获取,而后者是直接把共享数据发送到接收数据的程序。因此,在使用剪贴板通信方法时,接收数据程序的对话框上需要增加一个获取数据的命令按钮,而使用消息通信方法则不需要这个按钮。使用动态数据交换(DDE)通信动态数据交换(DynamicDataExchange,DDE 也是一种进程间通信形式。它最早是随着 Windows3.1 由美国微软公司提出的。当前大部分软件仍就支持 DDE 但近 10 年间微软公司已经停止发展 DDES
37、 术,只保持对 DDES 术给予兼容和支持。但我们仍然可以利用DDES 术编写自己的数据交换程序。使用 DDE 技术通信原理两个同时运行的程序间通过 DDET 式交换数据时是客户/服务器关系, 一旦客户和服务器建立起来连接关系,则当服务器中的数据发生变化后就会马上通知客户。通过 DDEST式建立的数据连接通道是双向的,即客户不但能够读取服务器中的数据,而且可以对其进行修改。DDEft 剪贴板一样既支持标准数据格式(如文本、位图等),又可以支持自定义的数据格式。但它们的数据传输机制却不同,一个明显区别是剪贴板操作几乎总是用作对用户指定操作的一次性应答,如从菜单中选择粘贴命令。尽管 DDE 也可以
38、由用户启动,但它继续发挥作用,一般不必用户进一步干预。DDEt 三种数据交换方式,即(1)冷连接(CoolLink):数据交换是一次性数据传输,与剪贴板相同。当服务器中的数据发生变化后不通知客户,但客户可以随时从服务器读写数据;(2)温连接(WarmLink):当服务器中的数据发生变化后马上通知客户,客户得到通知后将数据取回;(3)热连接(HotLink):当服务器中的数据发生变化后马上通知客户,同时将变化的数据直接送给客户。DDE 客户程序向 DDE 服务器程序请求数据时,它必须首先知道服务器的名称(即DDEService 名) 、 DDE题名称 (Topics 名) , 还要知道请求哪一个
39、数据项的项目名称 (Items名)。DDEService 名应该具有唯一性,否则容易产生混乱。通常 DDEService 就是服务器的程序名称,但不是绝对的,它是由程序设计人员在程序内部设定好的,并不是通过修改程序名称就可以改变的。Topics 名和 Items 名也是由 DDEService 在其内部设定好的,所有服务程序的 Service 名、Topics名都是注册在系统中,当一个客户向一个服务器请求数据时,客户必须向系统报告服务器的 Service 名和 Topics 名。 只有当 Service 名、 Topics 名与服务器内部设定的名称一致时,系统才将客户的请求传达给服务器。当服务
40、名和 Topics 名相符时,服务器马上判断 Items 名是否合法。如果请求的 Item名是服务器中的合法数据项,服务器即建立此项连接,建立连接的数据发生数值变化后,服务器会及时通知客户。一个服务器可以有多个 Topics 名,Items 名的数量也不受限制。DD 或换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的 DDE据格式, 进行应用程序之间特别目的 IPC,它们有更紧密耦合的通信要求。 大多数基于 Windows 的应用程序都支持 DDE1DDEt 个明显的缺点就是,通信效率低下,当通信量较大时数据刷新速度慢,在数据较少时 DDE 较实用。如何使用 DDEML
41、 编写程序早期的 DDES 于消息机制,应用程序间的消息传递需程序员调度。由于 DDE 消息通信牵涉的操作细节颇多,实现完全的 DD 助、议不是非常容易的事情,而且不同的开发者对协议的解释也略有不同。为了使用方便起见,微软提供 DDEt 理库(TheDDEManagementLibrary,简称 DDEML)DDEM 畛门协调 DDEffl 信,给 DDES 用程序提供句柄字符串和数据交换的服务,消除了早期由于 DD 助、议不一致所引起的问题。使用 DDEML 发的应用程序(客户/服务器)无论在运行一致性方面, 还是在程序相互通信方面,性能均优于没有使用 DDEM 的应用程序。而且 DDEM
42、的应用使得开发支持 DDE勺应用程序容易了许多, 因为 DDEML(这是个 DLL)担起了内务府总管的工作。 使用 DDEM质,实际上客户和服务器之间的多数会话并不是直达对方的,而是经由 DDEM 中转,即用Callback 函数处理 DDE易(Transaction),而早期的消息通信是直接的。在调用其他 DDEM 函数前,客户/服务器必须调用 DdeInitialize()函数,以获取实例标识符,注册 DDECallback 函数,并为 Callback 函数指定事务过滤。对于服务器,在使用DdeInitialize()初始化后,调用 DdeCreateStringHandle()建立 S
43、ervice 名、Topics 名和 Items 名等标识的句柄,再通过 DdeNameService()在操作系统中注册服务器的名字。根据这些句柄,客户就可以使用它提供的 DDE务了。为了执行某个 DDEB 务,许多 DDEM 函数需要获得字符串的访问权。例如:一个客户在调用 DdeConnect()函数来请求同服务器建立会话时,必须指定 Service 名和 Topics 名。可以通过调用 DdeCreateStringHandle。函数来获取特定字符串句柄。例如:HSZhszServName=DdeCreateStringHandle(idInst,MyServer,CP_WINANSI
44、);HSZhszSysTopic=DdeCreateStringHandle(idInst,SZDDESYS_TOPIC,CP_WINANSI);一个应用程序的 DDE0 调函数在大多 DDE务中接收多个字符串句柄。比如:在XTYP_REQUESW 处理期间,一个 DDE 服务器接收两个字符串句柄:一个标识 Topics名字符串,另一个标识 Items 名字符串。可以通过调用 DdeQueryString()函数来获取相应于字符串句柄的字符串长度,并且复制字符串到应用程序定义的 buffer 中。例如:DWORDidInst;DWORDcb;PSTRpszServName;cb=DdeQuer
45、yString(idInst,hszServ,(LPSTR)NULL,0,CP_WINANSI)+1;pszServName=(PSTR)LocalAlloc(LPTR,(UINT)cb);DdeQueryString(idInst,hszServ,pszServName,cb,CP_WINANSI);根据微软 MSDN有的基于消息 DD 刖、议的应用程序与 DDEM 应用程序是相容的,也就是说,基于消息通信的 DDES 用程序可以与 DDEM 应用程序对话和交易。在使用DDEM时, 必须在源程序文件中包括ddeml.h头文件, 连接user32.lib文件, 并保证ddeml.dll文件正确
46、的系统路径。使用 DDE 通信的实例由上面的介绍可知,可以编写基于消息 DDES 用程序,也可以编写应用 DDEML 的应用程序。对于前者,实现的方法较复杂,这里不做介绍。这里介绍一个应用 DDEM编写的 DDE!信实例。为了便于管理,这里把这个程序封装成一个 CMyDd 读,下面介 2这个类。CMyDde头文件如下:/DDE.h:定义 CMyDde 类/#ifndef_DDE_H_INCLUDED#define_DDE_H_INCLUDED#includeclassCMyDdepublic:CMyDde();CMyDde();/静态回调成员函数staticHDDEDATACALLBACKDd
47、eCallback(UINTiType,UINTiFmt,HDDEDATAhData,DWORDdwData1,DWORDdata2);voidDdeCall(UINTiType,LPCSTRszSvr,LPCSTRszTopic,LPCSTRszAtom);voidDdeServer(CStringstrReply);voidDdeClient(CStringstrRequest);CStringGetReply()returnm_strReply;CStringGetRequest()returnm_strRequest;private:staticCMyDde*fakeThis;DWOR
48、DidInst;CStringAppName;CStringm_strReply;CStringm_strRequest;#endif其中包含了 ddeml.h 头文件,DdeCallback()为 static 回调函数。之所以使用 static,是因为 DdeInitialize()函数的需要,否则编译会出错。对于服务程序,使用类中的 DdeServer()函数。在这个函数中用 DdeInitialize()调用回调函数 DdeCallback(),注册服务名 MyDDEService 以便客户程序与服务程序取得联系。在DdeInitialize()中设置事务过滤,例如以下的 DdeSer
49、ver()函数中,在 DdeInitialize()中设置CBF_FAIL_POKE“示 XTYP_POKES 事件将被过滤掉。DdeServer()函数的代码如下:voidCMyDde:DdeServer(CStringstrReply)m_strReply=strReply;fakeThis=this;/建立 DDEDdeInitialize(&idInst,DdeCallback,APPCLASS_STANDARD|CBF_FAIL_ADVISES|CBF_FAIL_POKES|CBF_SKIP_REGISTRATIONS|CBF_SKIP_UNREGISTRATIONS,0L)
50、;/注册服务名 MyDDEService,使该程序作为 DDE 服务器AppName=MyDDEService;HSZhszService=DdeCreateStringHandle(idInst,AppName,0);DdeNameService(idInst,hszService,NULL,DNS_REGISTER);回调函数(Callbackfunction)大量用于 Windows 的系统服务,通过它,程序员可以安装设备驱动程序和消息过滤系统,以控制 Windows 的有效使用。以下是 DDEK 务程序的回调函数源代码:HDDEDATACALLBACKCMyDde:DdeCallbac
51、k(UINTiType,UINTiFmt,HCONVhConv,HSZhsz1,/Topic.HSZhsz2,/atom.HDDEDATAhData,DWORDdwData1,DWORDdata2)charszBuffer100;switch(iType)/建立交易连接caseXTYP_CONNECT:/获得应用名DdeQueryString(fakeThis-idInst,hsz2,szBuffer,sizeof(szBuffer),0);/如果此应用不能被此服务器支持,返回 NULLif(strcmp(szBuffer,fakeThis-AppName)returnNULL;/获彳导top
52、ic 名DdeQueryString(fakeThis-idInst,hsz1,szBuffer,sizeof(szBuffer),0);/如果连接成功,返回 1return(HDDEDATA)1;caseXTYP_REQUEST:/获彳导topic 名DdeQueryString(fakeThis-idInst,hsz1,szBuffer,sizeof(szBuffer),0);if(strcmp(szBuffer,query)=0)/获彳导Item 名DdeQueryString(fakeThis-idInst,hsz2,szBuffer,sizeof(szBuffer),0);strcp
53、y(szBuffer,fakeThis-m_strReply);returnDdeCreateDataHandle(fakeThis-idInst,(LPBYTE)szBuffer,sizeof(szBuffer),0,hsz2,CF_TEXT,0);break;caseXTYP_EXECUTE:/获彳导topic 名DdeQueryString(fakeThis-idInst,hsz1,szBuffer,sizeof(szBuffer),0);if(strcmp(szBuffer,data)=0)/获得数据DdeGetData(hData,(LPBYTE)szBuffer,40L,0L);f
54、akeThis-m_strRequest=szBuffer;return(HDDEDATA)1;break;returnNULL;其中只使用了三个选项,即 XTYP_CONNECXTYP_REQUESTXTYP_EXECUTE,还有其他的一些选项,见微软的 MSD 证明。XTYP_CONNEg 于客户程序使用的DdeConnect()函数。XTYP_REQUESTXTYP_EXECUTBI响应于客户程序中使用DdeClientTransaction()函数的 XTYP_REQUESTXTYP_EXECUTE 项。在服务程序中,对于 XTYP_REQUESW,可以用 DdeCreateDataH
55、andle 函数向客户程序发送数据,而XTYP_EXECUT 环能。而对于 XTYP_EXECUTE,可以用 DdeGetData()函数从客户获取数据,而 XTYP_REQUE 所能。在服务程序中用 DdeQueryString()函数从客户程序中获得 Topics 名和 Items 名, 先得到 Topics 名,然后得到 Items 名。在本实例中 XTYP_REQUEST5 的 Topics 名是“query,Items 名为“1”,而 XTYP_EXECUTE 顷的 Topics 名是“data,Items 名为“1”,但 Items 名都没有被利用。以下是用于客户程序的主函数,也需要用 DdeInitialize()函数初始化,并设置过滤类型。其中使用了类型调用函数 DdeCall()。DdeClient()函数的代码
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 超高压输电线舞动监测装置安装合同二零二五
- 2024监理工程师考试必考考点试题及答案
- 2024人力资源管理师考试的时间管理试题及答案
- 学生安全防范意识教育
- 黑龙江省伊春市嘉荫县第一中学2025届高考原创信息试卷语文试题(二)含解析
- 植物的光合反应机制分析试题及答案
- 黑龙江省哈尔滨市保国第二小学2025届数学三下期末教学质量检测模拟试题含解析
- 黑龙江省大兴安岭地区漠河县2025届三年级数学第二学期期末统考模拟试题含解析
- 黑龙江省绥化市明水县2024-2025学年数学五年级第二学期期末达标测试试题含答案
- 黑龙江省鸡西市2024-2025学年数学四年级第二学期期末学业质量监测模拟试题含解析
- 信用风险度量第六章-KMV模型课件
- 小学硬笔书法课教案(1-30节)
- 基于CAN通讯的储能变流器并机方案及应用分析报告-培训课件
- 医院清洁消毒与灭菌课件
- 消防安装工程施工方案Word版
- 软管管理规定3篇
- 关于对领导班子的意见和建议
- 【课件】学堂乐歌 课件-2022-2023学年高中音乐人音版(2019)必修音乐鉴赏
- 纳布啡在胃肠镜麻醉中的临床观察-课件
- 常用手术器械手工清洗
- 2022中西医执业医师实践技能疾病对照诊断内科
评论
0/150
提交评论