Windows DDE通讯(服务器/客户端模式)在Windows系统中,各个应用程序(进程)之间常常需要交换、传递数据,这就要解决进程间的数据通信问题。在最初的16位Windows3.x系统中,所有Windows应用程序共享单一地址,任何进程都能够对这一共享地址空间的数据进行读写操作。随着Windwos98、WindowsNT、Windows2000等32位的操作系统的出现,规定每个进程都有自己的地址空间,一个Windows进程不能存取另一个进程的私有数据,也就是说,虽然两个进程可以用具有相同值的指针寻址,但所读写的只是它们各自的数据,这样就减少了进程之间的相互干扰。那么上述技术的采用是否意味着各个应用程序之间不能进行数据交换了呢?答案当然是否定的,强大的Windows系统早已为我们设计了很多方案来解决进行间的通信问题,这里我们只探讨如何通过动态数据交换(DDE)方法实现进程间的数据通信。本实例程序功能如下,服务器端有两个数据项,一个是输入的字符串,另一个是定时增加的整数。运行该程序的两个实例后,两个程序就可以建立DDE连接,实现数据的传递,并将另外一个实例传送过来的数据显示出来。下图为程序编译运行后的效果图:图一、DDE方法实现进程间数据通信程序的界面效果图一、实现方法自从微软推出Windows操作系统以来,动态数据交换(DDE)就已经成为Windows的部分,并且很多Windwos应用程序都使用了DDE技术来实现进程之间的数据交换。DDE是建立在Windows内部消息系统、全局和共享全局内存基础上的一种协议,用来协调Windows应用程序之间的数据交换和命令调用,它已经成为应用程序之间通信的一种常用方法。DDE应用程序可以分为四种类型:客户类型、服务器类型、客户/服务器类型和监视器。DDE会话发生在客户应用程序和服务器应用程序之间。客户应用程序从服务器应用程序请求数据或服务,服务器应用程序响应客户应用程序的数据或服务请求。客户/服务器应用程序是既可以发出请求,又可以提供信息,监视器应用程序则是用语调试的目的。DDE协议使用三级树型命名:服务(SERVICE)、主题(TOPIC)和数据项(ITEM)来标识DDE所要传送的数据单元。服务使应用程序具有了提供给其他程序的数据交换能力;主题类似于目录,是建立会话连接的参数:ITEM才是DDE具体通信时要传送的数据内容,比如一个数据或一个字符串。动态交换管理库(DDEML)提供了DDE和应用程序级协议。使用DDEML开发的应用程序无论是在运行一致性方面还是在应用程序相互通信方面性能均优于没有使用DDEML的应用程序。而且DDEML的应用使得开发支持DDE的应用程序容易了许多。建立DDE会话后,客户程序和服务器程序可以通过三种链接方式进行数据交换,分别是:1、冷链接:客户程序申请数据,服务器程序立即给客户程序发送数据;2、温链接:服务器程序通知客户程序数据数据项发生了改变,但是并没有将已发生的值发送给客户程序。3、热链接:当数据项发生变化时,服务器程序立即把变化后的值发送给客户程序,这是最常用、最方便的方法,下面的例子就使用的这种方法。 DDE编程过程:A、DDE会话初始化:使用API函数DdeInitialize(),在DDEML中注册应用。B、会话建立服务器:注册服务DdeNameService.客户:连接DdeConnect.C、会话过程类似于Windows的消息循环,会话的过程就是事务处理的过程。客户通过DdeClientTransaction()来发出事务请求,通过DDE回调函数,服务器处理客户事务请求,返回DdeCreateDataHandle来发送数据,同时客户可以调用DdeGetData()获取数据。D、会话结束可由服务方或客户方来终止会话,推出程序时要注消服务,释放资源,调用DdeUninitialize()。 二、编程步骤1、启动Visual C++6.0,新建一个基于对话框的MFC应用程序,取名为DDEdemo,添加两个Group Box控件并分别在其上放置编辑控件IDC_EDIT、静态控件ID_STATIC1、ID_STATIC2、 ID_STATIC3,用Wizard添加对应成员变m_edit(CString类型),添加并将其Caption置空,最后的界面如图一所示;2、使用CLASSWIZARD添加对话框函数,分别为WM_DESTORY、WM_INITDIALOG、WM_TIMER及IDC_EDIT的EN_CHANGE消息建立对应函数;3、在DDEdemoDlg.CPP中加入#include "ddel.h"以使用DDEML函数。并添加以下宏定义和全局变量:#define NITEM 2const char szApp[] ="Server";const char szTopic[]= "Topic";//SERVER ITEM名称字符串数组const char*pszItem[NITEM] = {"Item1","Item2"};int count = 1;//计数//存放服务器中的数据项内容CStringServerData[NITEM];HCONV hConv = 0;DWORD idInst = 0;HWND hWnd;HANDLE hInst;HSZ hszApp = 0;HSZ hszTopic = 0;HSZ hszItem[NITEM];BOOL bConnect;编写代码:1、回调函数定义HDDEDATA CALLBACKDdeCallback(UINT wType,UINT wFmt,HCONV hConv,HSZ Topic,HSZ Item, HDDEDATA hData,DWORD lData1,DWORD lData2){ int I; char tmp[255]; switch(wType) { case XTYP_ADVSTART: case XTYP_CONNECT: return ((HDDEDATA)TRUE); case XTYP_ADVDATA://有数据到来 for(I=0;I if(Item==hszItem[I]) { DdeGetData(hData,(PBYTE)tmp,255,0);//取得数据 switch(I) { case 0: SetDlgItemText(hWnd,IDC_STATIC2,tmp); break; case 1: SetDlgItemText(hWnd,IDC_STATIC3,tmp); break; } } return ((HDDEDATA)DDE_FACK);//回执 case XTYP_REQUEST://数据请求 break; case XTYP_ADVREQ: for(I=0;I if(Item==hszItem[I]) { returnDdeCreateDataHandle(idInst,(PBYTE)(LPCTSTR)(ServerData[I]), ServerData[I].GetLength()+1,0,Item,wFmt,0); } } return 0;}2、对话框初始化BOOLCDdedemoDlg::OnInitDialog(){ CDialog::OnInitDialog(); // Add "About..." menu item tosystem menu. // IDM_ABOUTBOX must be in the systemcommand range. ASSERT((IDM_ABOUTBOX & 0xFFF0) ==IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX,strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here hWnd = m_hWnd; if(DdeInitialize(&idInst,(PFNCALLBACK)DdeCallback,APPCMD_FILTERINITS| CBF_FAIL_EXECUTES|CBF_SKIP_CONNECT_CONFIRMS|CBF_FAIL_SELFCONNECTIONS| CBF_FAIL_POKES,0)) { MessageBox("DDE SERVER初始化失败!"); return FALSE; } hInst=AfxGetApp()->m_hInstance; //创建DDE string hszApp=DdeCreateStringHandle(idInst,szApp,0); hszTopic=DdeCreateStringHandle(idInst,szTopic,0); for(int I=0;I hszItem[I]=DdeCreateStringHandle(idInst,pszItem[I],0); //注册服务; DdeNameService(idInst,hszApp,0,DNS_REGISTER); bConnect=FALSE; SetTimer(1,1000,NULL);//开始定时; return TRUE; // return TRUE unless you set the focus to a control}3、销毁DDE过程voidCDdedemoDlg::OnDestroy(){ CDialog::OnDestroy(); // TODO: Add your message handler codehere KillTimer(1);//销毁定时; DdeNameService(idInst,0,0,DNS_UNREGISTER);//注销服务; DdeFreeStringHandle(idInst,hszApp); DdeFreeStringHandle(idInst,hszTopic); for(int I=0;I DdeFreeStringHandle(idInst,hszItem[I]); DdeUninitialize(idInst); }4、通讯过程voidCDdedemoDlg::OnTimer(UINT nIDEvent){ // TODO: Add your message handler codehere and/or call default count++; ServerData[1].Format("%d",count); SetDlgItemText(IDC_STATIC1,ServerData[1]); DdePostAdvise(idInst,hszTopic,hszItem[1]);//通知更新; if(!bConnect)//如果没有建立连接 { hConv=DdeConnect(idInst,hszApp,hszTopic,NULL); //连接服务器端; if(hConv) //如果建立成功 { DWORD dwResult; bConnect=TRUE; for(int I=0;I DdeClientTransaction(NULL,0,hConv,hszItem[I],CF_TEXT,XTYP_ADVSTART, TIMEOUT_ASYNC,&dwResult); } } CDialog::OnTimer(nIDEvent);}voidCDdedemoDlg::OnChangeEdit1(){ // TODO: If this is a RICHEDIT control,the control will not // send this notification unless youoverride the CDialog::OnInitDialog() // function and callCRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into themask. // TODO: Add your control notificationhandler code here UpdateData(); ServerData[0]=m_edit; DdePostAdvise(idInst,hszTopic,hszItem[0]);//通知DDE更新该数据项目;} 11-08 02:23