Windows 进程通信 之 DDE技术
DDE (Dynamic Data Exchange,DDE)动态数据交换,是一种进程间通信机制,它最早是随着Windows由微软提出的。当前大部分软件仍旧支持DDE,但最近十年里微软已经停止发展DDE技术,只保持对它给予兼容和支持。但程序猿仍可以利用DDE技术来编写自己的数据交换程序。
一、使用DDE技术通信原理
两个同时运行的程序间通过DDE方式交换数据是C/S关系(客户端/服务器),一旦客户和服务器建立连接关系,则当服务器中的数据发生变化后就会马上通知客户端。通过DDE方式建立的数据连接通道是双向的,即客户端不仅能够读取服务器中的数据,而且可以对其进行修改。
DDE和剪贴板一样既支持标准数据格式(如文本、位图等),又可以支持自定义的数据格式.但它们的数据传输机制却不同,一个明显区别是剪贴板操作几乎总是用作对用户指定操作的一次性应答,如从菜单中选择粘贴命令。尽管DDE也可以由用户启动,但它继续发挥作用,一般不必用户进一步干预。
DDE有三种数据交换方式:
(1):冷连接(Cool Link):数据交换是一次性数据传输,当服务器中的数据发生变化后不通知客户,但是客户可以随时从服务器读写数据。
(2):温连接(Warm Link):当服务器中的数据发生变换后马上通知客户端,客户得到通知后将数据取回。
(3):热连接(Hot Link):当服务器中的数据发生变化后马上通知客户端,同事将变化的数据直接发送给客户。
DDE客户程序向DDE服务器程序请求数据时,必须首先知道服务器的名称(DDE Service名)、DDE主题名(Topics名)、请求哪一个数据项的项目名称(Items名)。
DDE Server名应该具有唯一性,否则容易产生混乱。通常DDE Service就是服务器的程序名,不是绝对的,它是由程序设计人员在程序内部设定好的,并不是通过修改程序名称就可以改变的。
Topics名和Items名也是由DDE Service在其内部设定好的,所有服务程序的Service名、Topics名都是注册在系统中,当一个客户向一个服务器请求数据时,客户必须向系统报告服务器的Service名和Topics名。只有当Service名、Topics名与服务器内部设定的名称一致时,系统才将客户的请求传达给服务器。
当服务名和Topics名相符时,服务器马上判断Items名是否合法。如果请求的Item名是服务器中的合法数据项,服务器即建立此项连接,建立连接的数据发生数值变化后,服务器会及时通知客户。一个服务器可以有多个Topics名,Items名的数量也不受限制。
DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式,进行应用程序之间特别目的IPC,它们有更紧密耦合的通信要求。大多数基于Windows的应用程序都支持DDE。但DDE有个明显的缺点就是,通信效率低下,当通信量较大时数据刷新速度慢,在数据较少时 DDE较实用。
二、如何使用DDEML编写程序
早期的DDE基于消息机制,应用程序间的消息传递需程序员调度.由于DDE消息通信牵涉的操作细节颇多,实现完全的DDE协议不是非常容易的事情,而且不同的开发者对协议的解释也略有不同.为了使用方便起见,微软提供DDE管理库(TheDDE Management Library,简称DDEML).DDEML专门协调DDE通信,给DDE应用程序提供句柄字符串和数据交换的服务,消除了早期由于DDE协议不一致所引起的问题.
使用DDEML开发的应用程序(客户/服务器)无论在运行一致性方面,还是在程序相互通信方面,性能均优于没有使用DDEML的应用程序.而且DDEML的应用使得开发支持DDE的应用程序容易了许多,因为DDEML(这是个DLL)担起了内务府总管的工作.使用DDEML后,实际上客户和服务器之间的多数会话并不是直达对方的,而是经由DDEML中转,即用Callback函数处理DDE交易(Transaction),而早期的消息通信是直接的.在调用其他DDEML函数前,客户/服务器必须调用DdeInitialize()函数,以获取实例标识符,注册DDE Callback函数,并为Callback函数指定事务过滤.对于服务器,在使用DdeInitialize()初始化后,调用CreateStringHandle()建立Service名、Topics名和Items名等标识的句柄,再通过DdeNameService ()在操作系统中注册服务器的名字.根据这些句柄,客户就可以使用它提供的DDE服务了.
为了执行某个DDE任务,许多DDEML函数需要获得字符串的访问权.
例如:一个客户在调用DdeConnect()函数来请求同服务器建立会话时,必须指定Service名和Topics名.可以通过调用DdeCreateStringHandle()函数来获取特定字符串句柄.例如:
HSZ hszServName = DdeCreateStringHandle(idInst,"MyServer",CP_WINANSI);
HSZ hszSysTopic = DdeCreateStringHandle(idInst,SZDDESYS_TOPIC,CP_WINANSI);
一个应用程序的DDE回调函数在大多DDE事务中接收多个字符串句柄.比如:在XTYP_REQUEST事务处理期间,一个DDE
服务器接收两个字符串句柄:一个标识Topics名字符串,另一个标识Items名字符串.可以通过调用DdeQueryString()函数来获取相应于字符串句柄的字符串长度,并且复制字符串到应用程序定义的buffer中.
例如:
DWORD idInst;
DWORD cb;
HSZ hszServ;
PSTR pszServName;
cb = DdeQueryString(idInst, hszServ, (LPSTR) NULL, 0, CP_WINANSI) + 1;
pszServName = (PSTR) LocalAlloc(LPTR, (UINT) cb);
DdeQueryString(idInst, hszServ, pszServName, cb, CP_WINANSI);
根据微软MSDN,现有的基于消息DDE协议的应用程序与DDEML应用程序是相容的,也就是说,基于消息通信的DDE应用程序可以与DDEML应用程序对话和交易.在使用DDEML时,必须在源程序文件中包括ddeml.h头文件,连接user32.lib文件,并保证ddeml.dll文件正确的系统路径.
使用DDE通信的实例
由上面的介绍可知,可以编写基于消息DDE应用程序,也可以编写应用DDEML的应用程序.对于前者,实现的方法较复杂,这里不做介绍.这里介绍一个应用DDEML编写的DDE通信实例.
为了便于管理,这里把这个程序封装成一个CMyDde类,下面介绍这个类.CMyDde类头文件如下:
// DDE.h: 定义CMyDde类//
#ifndef _DDE_H_INCLUDED
#define _DDE_H_INCLUDED
#include <ddeml.h>
class CMyDde
{
public:
CMyDde();
~CMyDde();
// 静态回调成员函数
static HDDEDATA CALLBACK DdeCallback(UINT iType,UINT iFmt,
HCONV hConv,HSZ hsz1,HSZ hsz2,
HDDEDATA hData,DWORD dwData1,DWORD data2);
void DdeCall(UINT iType, LPCSTR szSvr,LPCSTR szTopic,LPCSTR szAtom);
void DdeServer(CString strReply);
void DdeClient(CString strRequest);
CString GetReply() return m_strReply;
CString GetRequest() return m_strRequest;
private:
static CMyDde* fakeThis;
DWORD idInst;
CString AppName;
CString m_strReply;
CString m_strRequest;
};
#endif
其中包含了ddeml.h头文件,DdeCallback()为static回调函数.之所以使用static,是因为DdeInitialize()函数的需要,否则编译会出错.
对于服务程序,使用类中的DdeServer()函数.在这个函数中用DdeInitialize()调用回调函数DdeCallback(),注册服务名MyDDEService,以便客户程序与服务程序取得联系.在DdeInitialize()中设置事务过滤,例如以下的DdeServer()函数中,在DdeInitialize()中设置CBF_FAIL_POKES,表示XTYP_
POKES事件将被过滤掉.DdeServer()函数的代码 如下:
void CMyDde::DdeServer(CString strReply)
CBF_FAIL_POKES
回调函数(Callback
function)大量用于Windows的系统服务,通过它,程序员可以安装设备驱动程序和消息过滤系统,以控制Windows的有效使用.以下是DDE服务程序的回调函数源代码:
HDDEDATA CALLBACK CMyDde::DdeCallback(UINT iType,
UINT iFmt,HCONV hConv,
HSZ hsz1, // Topic.
HSZ hsz2, // atom.
HDDEDATA hData,DWORD dwData1,DWORD data2)
{
char szBuffer[100];
switch(iType)
{
// 建立交易连接
case XTYP_CONNECT:
// 获得应用名
DdeQueryString(fakeThis->idInst,hsz2,
szBuffer,sizeof(szBuffer),0);
// 如果此应用不能被此服务器支持,返回NULL
if(strcmp(szBuffer,fakeThis->AppName)) return NULL;
// 获得topic名DdeQueryString(fakeThis->idInst,hsz1,
szBuffer,sizeof(szBuffer),0);
// 如果连接成功,返回1
return (HDDEDATA)1;
case XTYP_REQUEST:
// 获得topic名DdeQueryString(fakeThis->idInst,hsz1,
szBuffer,sizeof(szBuffer),0);
if(strcmp(szBuffer,"query")==0)
// 获得Item 名DdeQueryString(fakeThis->idInst,hsz2,
szBuffer,sizeof(szBuffer),0);
strcpy(szBuffer,fakeThis->m_strReply);
return DdeCreateDataHandle(fakeThis->idInst,
(LPBYTE)szBuffer,sizeof(szBuffer),0,hsz2,CF_TEXT,0);
break;
case XTYP_EXECUTE:
// 获得topic名DdeQueryString(fakeThis->idInst,hsz1,
szBuffer,sizeof(szBuffer),0);
if(strcmp(szBuffer,"data")==0)
// 获得数据
DdeGetData(hData, (LPBYTE)szBuffer, 40L, 0L);
fakeThis->m_strRequest=szBuffer;
return (HDDEDATA)1;
break;
}
return NULL;
}
其中只使用了三个选项,即XTYP_CONNECT、XTYP_REQUEST和XTYP_
EXECUTE,还有其他的一些选项,见微软的MSDN说明.XTYP_CONNECT响应于客户程序使用的DdeConnect()函数. XTYP_REQUEST和XTYP_EXECUTE分别响应于客户程序中使用DdeClientTransaction()函数的 XTYP_REQUEST和XTYP_
EXECUTE选项.在服务程序中,对于XTYP_REQUEST选项,可以用DdeCreateDataHandle函数向客户程序发送数据,而XTYP_EXECUTE则不能.而对于XTYP_EXECUTE选项,可以用DdeGetData()函数从客户获取数据,而XTYP_REQUEST 则不能.
在服务程序中用DdeQueryString()函数从客户程序中获得Topics名和Items名,先得到Topics名,然后得到Items名.在本实例中XTYP_REQUEST选项的Topics名是"query",Items名为"1",而XTYP_EXECUTE选项的Topics名是 "data",Items名为"1",但Items名都没有被利用.
以下是用于客户程序的主函数,也需要用DdeInitialize()函数初始化,并设置过滤类型.其中使用了类型调用函数DdeCall().DdeClient()函数的代码如下:
void CMyDde::DdeClient(CString strRequest)
CBF_SKIP_UNREGISTRATIONS,0L);
DdeCall(XTYP_EXECUTE,TEXT("MyDDEService"),TEXT("data"),TEXT("1"));
DdeCall(XTYP_REQUEST,TEXT("MyDDEService"),TEXT("query"),TEXT("1"));
在类型调用的DdeCall()函数中,首先获得Service名、Topics名和Items名的字符串句柄,然后用DdeConnect()函数与服务程序连接.如果连接成功,就可以用DdeClientTransaction()
函数和用XTYP_REQUEST和XTYP_EXECUTE类型向服务程序发送数据.其中,对于XTYP_REQUEST,可以用DdeGetData ()函数从服务程序获得数据.最后用DdeDisconnect()函数断开与服务程序的连接,并且用DdeFreeStringHandle()函数释放Service名、Topics名和Items名的字符串句柄.DdeCall()函数的源代码如下:
void CMyDde::DdeCall(UINT iType,LPCSTR szSvr,LPCSTR szTopic,LPCSTR szItem)
{
HSZ hszServName = DdeCreateStringHandle(idInst,szSvr,CP_WINANSI);
HSZ hszTopic = DdeCreateStringHandle(idInst,szTopic,CP_WINANSI);
HSZ hszItem = DdeCreateStringHandle(idInst,szItem,CP_WINANSI);
HCONV hConv= DdeConnect(idInst,hszServName,hszTopic,NULL);
HDDEDATA hData;
DWORD dwResult;
char szBuffer[100];
DWORD dwLength;
switch(iType)
case XTYP_REQUEST:
// 向服务器发送请求
hData = DdeClientTransaction(NULL,0,hConv,
hszItem, CF_TEXT, iType, 5000, &dwResult);
// 从服务器取得返回值
dwLength = DdeGetData(hData, (LPBYTE)szBuffer,sizeof(szBuffer), 0);
if (dwLength > 0)
m_strReply=szBuffer;
break;
case XTYP_EXECUTE:
strcpy(szBuffer,m_strRequest);
// 向服务器发送执行命令
hData = DdeClientTransaction((LPBYTE)szBuffer,
sizeof(szBuffer), hConv,
hszItem, CF_TEXT, iType, 5000, &dwResult);
break;
DdeDisconnect(hConv);
DdeFreeStringHandle(idInst,hszServName);
DdeFreeStringHandle(idInst,hszTopic);
DdeFreeStringHandle(idInst,hszItem);
}
Windows 进程通信 之 DDE技术的更多相关文章
- 几种Windows进程通信
32位Windows采用虚拟内存技术使每个进程虚拟4G内存,在逻辑上实现了对进程之间数据代码的分离与保护.那么相应的进程之间的通信也就有必要整理掌握一下. Windows进程间通讯的方法有很多:管道. ...
- Windows进程通信 -- 共享内存(1)
共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 W ...
- Windows进程通信 -- 共享内存
享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Wi ...
- Windows进程通信之一看就懂的匿名管道通信
目录 进程通信之一看就懂的匿名管道通信 一丶匿名管道 1.1何为匿名管道 1.2创建匿名管道需要注意的事项 1.3 创建匿名管道需要的步骤 1.4代码例子 1.5代码运行截图 进程通信之一看就懂的匿名 ...
- Windows进程通信-共享内存空间
三个模块 1,game.exe,三个方法,控制台输入指令('A','B','R')分别控制三个方法的调用: 2,WGDll.dll,要注入到game进程中的dll文件: 3,myconsole.exe ...
- windows进程通信 -- WM_COPYDATA消息
WM_COPYDATA消息,在win32中用来进行进程间的数据传输. typedef struct tagCOPYDATASTRUCT { // cds DWORD dwData; DWORD cbD ...
- Windows线程+进程通信
一 Windows线程进程 1)定义 按照MS的定义, Windows中的进程简单地说就是一个内存中的可执行程序, 提供程序运行的各种资源. 进程拥有虚拟的地址空间, 可执行代码, 数据, 对象句柄集 ...
- [转]WINDOW进程通信的几种方式
windows进程通信的几种方式 1 文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待.因此,进程不必使用文件I/O操作,只需简单的指针 ...
- Windows进程间的通信
一.进程与进程通信 进程间通信(Interprocess Communication, IPC)是指不同的进程之间进行数据共享和数据交换. 二.进程间通信方式 1. 文件映射 注:文件映射是在多 ...
随机推荐
- python常错: join() 方法
描述 Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串. 语法 join()方法语法: str.join(sequence) 参数 sequence -- 要连接的 ...
- WCF之契约
消息交换的双方,为了进行消息交换,而定义的一些数据交换规则,称之为契约. 契约只约束规则,不管实现. 契约对客户端和服务器的要求. 服务器:定义和实现契约.构建ServiceHost实例,然后暴露En ...
- (转)TeamCity配置笔记
1.编译sln 2.发布网站 3.重复代码检测 4.代码分析 5.单元测试&覆盖率测试 查看代码覆盖率 7.代码签入时自动触发编译 8.通知 1.在teamcity安装目录中找到TrayNot ...
- Android开发之切换activity动画overridePendingTransition
原文地址:http://blog.sina.com.cn/s/blog_706c449f01011s3v.html overridePendingTransition 在startActivity() ...
- 《HTML5与CSS3基础教程》学习笔记 ——Four Day
第十六章 1. 输入和元素 电子邮件框 <input type="email"> 搜索框 <input type="search"> ...
- 存储过程及Comm.cs类的创建
2013-09-25 13:08:59 一.准备工作 首先创建一个数据库,如创建“试用期公务员管理”数据库:再创建一个Comm.cs类,添加代码如下: using System;using Syste ...
- age
#include<iostream> #include<math.h> #define pi 3.14 using namespace std; int main() { in ...
- Python的 is 运算符
1. is运算符判断的是同一性而不是相等性. #x和y都绑定到同一个列表,而z被绑定在另外一个具有相同数值和顺序的列表上 x = y = [1, 2, 3] z = [1, 2, 3] x == y ...
- 《ISCSI集中存储》RHEL6——CE
集中存储的作用: 服务端的多余的分区,客户端可以拿来存储数据,并且所存储的数据直接写在服务器的硬盘上,当客户端A崩溃时,其他客户端依旧可以从服务器端访问到客户端A存储的数据. 服务器配置: Iptab ...
- 【原】使用ajax的get异常获取数据的时候,IE浏览器总是有缓存
//HTML里有下面这样一段代码 //异步获取准备人信息 $.get("PrepSetpNew/PrepareMainCrew.ashx?Method=GetPrepUserInfo&quo ...