windows 进程通信(使用DDE)

转载:http://blog.163.com/lzf_edu_cn/blog/static/3235151220083144214512/

动态数据交换(Dynamic Data Exchange,DDE)也是一种进程间通信形式。它最早是随着Windows

3.1由美国微软公司提出的。当前大部分软件仍就支持DDE,但近10年间微软公司已经停止发展DDE技术,只保持对DDE技术给予兼容和支持。但我们仍然可以利用DDE技术编写自己的数据交换程序。
3.8.1  使用DDE技术通信原理
两个同时运行的程序间通过DDE方式交换数据时是客户/服务器关系,一旦客户和服务器建立起来连接关系,则当服务器中的数据发生变化后就会马上通知客户。通过DDE方式建立的数据连接通道是双向的,即客户不但能够读取服务器中的数据,而且可以对其进行修改。
DDE和剪贴板一样既支持标准数据格式(如文本、位图等),又可以支持自定义的数据格式。但它们的数据传输机制却不同,一个明显区别是剪贴板操作几乎总是 用作对用户指定操作的一次性应答,如从菜单中选择粘贴命令。尽管DDE也可以由用户启动,但它继续发挥作用,一般不必用户进一步干预。
DDE有三种数据交换方式,即
(1)冷连接(Cool Link):数据交换是一次性数据传输,与剪贴板相同。当服务器中的数据发生变化后不通知客户,但客户可以随时从服务器读写数据;
(2)温连接(Warm Link):当服务器中的数据发生变化后马上通知客户,客户得到通知后将数据取回;
(3)热连接(Hot Link):当服务器中的数据发生变化后马上通知客户,同时将变化的数据直接送给客户。
DDE 客户程序向DDE 服务器程序请求数据时,它必须首先知道服务器的名称(即DDE
Service名)、DDE主题名称(Topics名),还要知道请求哪一个数据项的项目名称(Items名)。DDE
Service名应该具有唯一性,否则容易产生混乱。通常DDE
Service就是服务器的程序名称,但不是绝对的,它是由程序设计人员在程序内部设定好的,并不是通过修改程序名称就可以改变的。Topics名和Items名也是由DDE
Service在其内部设定好的,所有服务程序的Service名、Topics名都是注册在系统中,当一个客户向一个服务器请求数据时,客户必须向系统 报告服务器的Service名和Topics名。只有当Service名、Topics名与服务器内部设定的名称一致时,系统才将客户的请求传达给服务 器。
当服务名和Topics名相符时,服务器马上判断Items名是否合法。如果请求的Item名是服务器中的合法数据项,服务器即建立此项连接,建立连接的数据发生数值变化后,服务器会及时通知客户。一个服务器可以有多个Topics名,Items名的数量也不受限制。
DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式,进行应用程序之间特别目的IPC,它们有更紧密耦合 的通信要求。大多数基于Windows的应用程序都支持DDE。但DDE有个明显的缺点就是,通信效率低下,当通信量较大时数据刷新速度慢,在数据较少时 DDE较实用。
3.8.2  如何使用DDEML编写程序
早期的DDE基于消息机制,应用程序间的消息传递需程序员调度。由于DDE消息通信牵涉的操作细节颇多,实现完全的DDE协议不是非常容易的事情,而且不同的开发者对协议的解释也略有不同。为了使用方便起见,微软提供DDE管理库(The
DDE Management Library,
简称DDEML)。DDEML专门协调DDE通信,给DDE应用程序提供句柄字符串和数据交换的服务,消除了早期由于DDE协议不一致所引起的问题。
使用DDEML开发的应用程序(客户/服务器)无论在运行一致性方面,还是在程序相互通信方面,性能均优于没有使用DDEML的应用程序。而且DDEML的应用使得开发支持DDE的应用程序容易了许多,因为
DDEML(这是个
DLL)担起了内务府总管的工作。使用DDEML后,实际上客户和服务器之间的多数会话并不是直达对方的,而是经由DDEML中转,即用Callback函数处理DDE交易(Transaction),而早期的消息通信是直接的。
在调用其他DDEML函数前,客户/服务器必须调用DdeInitialize()函数,以获取实例标识符,注册DDE
Callback函数,并为Callback函数指定事务过滤。对于服务器,在使用DdeInitialize()初始化后,调用 DdeCreateStringHandle()建立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)
{
    m_strReply=strReply;
    fakeThis=this;
    // 建立DDE
    DdeInitialize(&idInst,DdeCallback,APPCLASS_STANDARD|
        CBF_FAIL_ADVISES|
        CBF_FAIL_POKES|
        CBF_SKIP_REGISTRATIONS|
        CBF_SKIP_UNREGISTRATIONS,0L);
    // 注册服务名MyDDEService,使该程序作为DDE服务器
    AppName="MyDDEService";
    HSZ hszService=DdeCreateStringHandle(idInst,AppName,0);
    DdeNameService(idInst,hszService,NULL,DNS_REGISTER);
}
回调函数(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)
{
    m_strRequest=strRequest;
    idInst=0;
    DdeInitialize(&idInst,NULL,APPCLASS_STANDARD|
        CBF_FAIL_ADVISES|
        CBF_FAIL_POKES|
        CBF_SKIP_REGISTRATIONS|
        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);
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值