windows 进程通信(使用DDE)
動(dòng)態(tài)數(shù)據(jù)交換(Dynamic Data Exchange,DDE)也是一種進(jìn)程間通信形式。它最早是隨著Windows
3.1由美國(guó)微軟公司提出的。當(dāng)前大部分軟件仍就支持DDE,但近10年間微軟公司已經(jīng)停止發(fā)展DDE技術(shù),只保持對(duì)DDE技術(shù)給予兼容和支持。但我們?nèi)匀豢梢岳肈DE技術(shù)編寫自己的數(shù)據(jù)交換程序。
3.8.1? 使用DDE技術(shù)通信原理
兩個(gè)同時(shí)運(yùn)行的程序間通過DDE方式交換數(shù)據(jù)時(shí)是客戶/服務(wù)器關(guān)系,一旦客戶和服務(wù)器建立起來連接關(guān)系,則當(dāng)服務(wù)器中的數(shù)據(jù)發(fā)生變化后就會(huì)馬上通知客戶。通過DDE方式建立的數(shù)據(jù)連接通道是雙向的,即客戶不但能夠讀取服務(wù)器中的數(shù)據(jù),而且可以對(duì)其進(jìn)行修改。
DDE和剪貼板一樣既支持標(biāo)準(zhǔn)數(shù)據(jù)格式(如文本、位圖等),又可以支持自定義的數(shù)據(jù)格式。但它們的數(shù)據(jù)傳輸機(jī)制卻不同,一個(gè)明顯區(qū)別是剪貼板操作幾乎總是 用作對(duì)用戶指定操作的一次性應(yīng)答,如從菜單中選擇粘貼命令。盡管DDE也可以由用戶啟動(dòng),但它繼續(xù)發(fā)揮作用,一般不必用戶進(jìn)一步干預(yù)。
DDE有三種數(shù)據(jù)交換方式,即
(1)冷連接(Cool Link):數(shù)據(jù)交換是一次性數(shù)據(jù)傳輸,與剪貼板相同。當(dāng)服務(wù)器中的數(shù)據(jù)發(fā)生變化后不通知客戶,但客戶可以隨時(shí)從服務(wù)器讀寫數(shù)據(jù);
(2)溫連接(Warm Link):當(dāng)服務(wù)器中的數(shù)據(jù)發(fā)生變化后馬上通知客戶,客戶得到通知后將數(shù)據(jù)取回;
(3)熱連接(Hot Link):當(dāng)服務(wù)器中的數(shù)據(jù)發(fā)生變化后馬上通知客戶,同時(shí)將變化的數(shù)據(jù)直接送給客戶。
DDE 客戶程序向DDE 服務(wù)器程序請(qǐng)求數(shù)據(jù)時(shí),它必須首先知道服務(wù)器的名稱(即DDE
Service名)、DDE主題名稱(Topics名),還要知道請(qǐng)求哪一個(gè)數(shù)據(jù)項(xiàng)的項(xiàng)目名稱(Items名)。DDE
Service名應(yīng)該具有唯一性,否則容易產(chǎn)生混亂。通常DDE
Service就是服務(wù)器的程序名稱,但不是絕對(duì)的,它是由程序設(shè)計(jì)人員在程序內(nèi)部設(shè)定好的,并不是通過修改程序名稱就可以改變的。Topics名和Items名也是由DDE
Service在其內(nèi)部設(shè)定好的,所有服務(wù)程序的Service名、Topics名都是注冊(cè)在系統(tǒng)中,當(dāng)一個(gè)客戶向一個(gè)服務(wù)器請(qǐng)求數(shù)據(jù)時(shí),客戶必須向系統(tǒng) 報(bào)告服務(wù)器的Service名和Topics名。只有當(dāng)Service名、Topics名與服務(wù)器內(nèi)部設(shè)定的名稱一致時(shí),系統(tǒng)才將客戶的請(qǐng)求傳達(dá)給服務(wù) 器。
當(dāng)服務(wù)名和Topics名相符時(shí),服務(wù)器馬上判斷Items名是否合法。如果請(qǐng)求的Item名是服務(wù)器中的合法數(shù)據(jù)項(xiàng),服務(wù)器即建立此項(xiàng)連接,建立連接的數(shù)據(jù)發(fā)生數(shù)值變化后,服務(wù)器會(huì)及時(shí)通知客戶。一個(gè)服務(wù)器可以有多個(gè)Topics名,Items名的數(shù)量也不受限制。
DDE交換可以發(fā)生在單機(jī)或網(wǎng)絡(luò)中不同計(jì)算機(jī)的應(yīng)用程序之間。開發(fā)者還可以定義定制的DDE數(shù)據(jù)格式,進(jìn)行應(yīng)用程序之間特別目的IPC,它們有更緊密耦合 的通信要求。大多數(shù)基于Windows的應(yīng)用程序都支持DDE。但DDE有個(gè)明顯的缺點(diǎn)就是,通信效率低下,當(dāng)通信量較大時(shí)數(shù)據(jù)刷新速度慢,在數(shù)據(jù)較少時(shí) DDE較實(shí)用。
3.8.2? 如何使用DDEML編寫程序
早期的DDE基于消息機(jī)制,應(yīng)用程序間的消息傳遞需程序員調(diào)度。由于DDE消息通信牽涉的操作細(xì)節(jié)頗多,實(shí)現(xiàn)完全的DDE協(xié)議不是非常容易的事情,而且不同的開發(fā)者對(duì)協(xié)議的解釋也略有不同。為了使用方便起見,微軟提供DDE管理庫(kù)(The
DDE Management Library,
簡(jiǎn)稱DDEML)。DDEML專門協(xié)調(diào)DDE通信,給DDE應(yīng)用程序提供句柄字符串和數(shù)據(jù)交換的服務(wù),消除了早期由于DDE協(xié)議不一致所引起的問題。
使用DDEML開發(fā)的應(yīng)用程序(客戶/服務(wù)器)無論在運(yùn)行一致性方面,還是在程序相互通信方面,性能均優(yōu)于沒有使用DDEML的應(yīng)用程序。而且DDEML的應(yīng)用使得開發(fā)支持DDE的應(yīng)用程序容易了許多,因?yàn)?br /> DDEML(這是個(gè)
DLL)擔(dān)起了內(nèi)務(wù)府總管的工作。使用DDEML后,實(shí)際上客戶和服務(wù)器之間的多數(shù)會(huì)話并不是直達(dá)對(duì)方的,而是經(jīng)由DDEML中轉(zhuǎn),即用Callback函數(shù)處理DDE交易(Transaction),而早期的消息通信是直接的。
在調(diào)用其他DDEML函數(shù)前,客戶/服務(wù)器必須調(diào)用DdeInitialize()函數(shù),以獲取實(shí)例標(biāo)識(shí)符,注冊(cè)DDE
Callback函數(shù),并為Callback函數(shù)指定事務(wù)過濾。對(duì)于服務(wù)器,在使用DdeInitialize()初始化后,調(diào)用 DdeCreateStringHandle()建立Service名、Topics名和Items名等標(biāo)識(shí)的句柄,再通過DdeNameService ()在操作系統(tǒng)中注冊(cè)服務(wù)器的名字。根據(jù)這些句柄,客戶就可以使用它提供的DDE服務(wù)了。
為了執(zhí)行某個(gè)DDE任務(wù),許多DDEML函數(shù)需要獲得字符串的訪問權(quán)。例如:一個(gè)客戶在調(diào)用DdeConnect()函數(shù)來請(qǐng)求同服務(wù)器建立會(huì)話時(shí),必須 指定Service名和Topics名。可以通過調(diào)用DdeCreateStringHandle()函數(shù)來獲取特定字符串句柄。例如:
HSZ hszServName = DdeCreateStringHandle(idInst,"MyServer",CP_WINANSI);
HSZ hszSysTopic = DdeCreateStringHandle(idInst,SZDDESYS_TOPIC,CP_WINANSI);
一個(gè)應(yīng)用程序的DDE回調(diào)函數(shù)在大多DDE事務(wù)中接收多個(gè)字符串句柄。比如:在XTYP_REQUEST事務(wù)處理期間,一個(gè)DDE
服務(wù)器接收兩個(gè)字符串句柄:一個(gè)標(biāo)識(shí)Topics名字符串,另一個(gè)標(biāo)識(shí)Items名字符串。可以通過調(diào)用DdeQueryString()函數(shù)來獲取相應(yīng)于字符串句柄的字符串長(zhǎng)度,并且復(fù)制字符串到應(yīng)用程序定義的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);
根據(jù)微軟MSDN,現(xiàn)有的基于消息DDE協(xié)議的應(yīng)用程序與DDEML應(yīng)用程序是相容的,也就是說,基于消息通信的DDE應(yīng)用程序可以與DDEML應(yīng)用程序 對(duì)話和交易。在使用DDEML時(shí),必須在源程序文件中包括ddeml.h頭文件,連接user32.lib文件,并保證ddeml.dll文件正確的系統(tǒng) 路徑。
使用DDE通信的實(shí)例
由上面的介紹可知,可以編寫基于消息DDE應(yīng)用程序,也可以編寫應(yīng)用DDEML的應(yīng)用程序。對(duì)于前者,實(shí)現(xiàn)的方法較復(fù)雜,這里不做介紹。這里介紹一個(gè)應(yīng)用DDEML編寫的DDE通信實(shí)例。
為了便于管理,這里把這個(gè)程序封裝成一個(gè)CMyDde類,下面介紹這個(gè)類。CMyDde類頭文件如下:
// DDE.h: 定義CMyDde類
//
#ifndef _DDE_H_INCLUDED
#define _DDE_H_INCLUDED
#include <ddeml.h>
class CMyDde?
{
public:
??? CMyDde();
??? ~CMyDde();
??? // 靜態(tài)回調(diào)成員函數(shù)
??? 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回調(diào)函數(shù)。之所以使用static,是因?yàn)镈deInitialize()函數(shù)的需要,否則編譯會(huì)出錯(cuò)。
對(duì)于服務(wù)程序,使用類中的DdeServer()函數(shù)。在這個(gè)函數(shù)中用DdeInitialize()調(diào)用回調(diào)函數(shù)DdeCallback(),注冊(cè)服務(wù) 名MyDDEService,以便客戶程序與服務(wù)程序取得聯(lián)系。在DdeInitialize()中設(shè)置事務(wù)過濾,例如以下的DdeServer()函數(shù) 中,在DdeInitialize()中設(shè)置CBF_FAIL_POKES,表示XTYP_
POKES事件將被過濾掉。DdeServer()函數(shù)的代碼?? 如下:
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);
??? // 注冊(cè)服務(wù)名MyDDEService,使該程序作為DDE服務(wù)器
??? AppName="MyDDEService";
??? HSZ hszService=DdeCreateStringHandle(idInst,AppName,0);
??? DdeNameService(idInst,hszService,NULL,DNS_REGISTER);
}
回調(diào)函數(shù)(Callback
function)大量用于Windows的系統(tǒng)服務(wù),通過它,程序員可以安裝設(shè)備驅(qū)動(dòng)程序和消息過濾系統(tǒng),以控制Windows的有效使用。以下是DDE服務(wù)程序的回調(diào)函數(shù)源代碼:
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:
??????? // 獲得應(yīng)用名
??????? DdeQueryString(fakeThis->idInst,hsz2,
??????????? szBuffer,sizeof(szBuffer),0);
???????
??????? // 如果此應(yīng)用不能被此服務(wù)器支持,返回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)
??????? {
??????????? // 獲得數(shù)據(jù)
??????????? DdeGetData(hData, (LPBYTE)szBuffer, 40L, 0L);
??????????? fakeThis->m_strRequest=szBuffer;
??????????? return (HDDEDATA)1;
??????? }
??????? break;
??? }
???
??? return NULL;
}
其中只使用了三個(gè)選項(xiàng),即XTYP_CONNECT、XTYP_REQUEST和XTYP_
EXECUTE,還有其他的一些選項(xiàng),見微軟的MSDN說明。XTYP_CONNECT響應(yīng)于客戶程序使用的DdeConnect()函數(shù)。 XTYP_REQUEST和XTYP_EXECUTE分別響應(yīng)于客戶程序中使用DdeClientTransaction()函數(shù)的 XTYP_REQUEST和XTYP_
EXECUTE選項(xiàng)。在服務(wù)程序中,對(duì)于XTYP_REQUEST選項(xiàng),可以用DdeCreateDataHandle函數(shù)向客戶程序發(fā)送數(shù)據(jù),而 XTYP_EXECUTE則不能。而對(duì)于XTYP_EXECUTE選項(xiàng),可以用DdeGetData()函數(shù)從客戶獲取數(shù)據(jù),而XTYP_REQUEST 則不能。
在服務(wù)程序中用DdeQueryString()函數(shù)從客戶程序中獲得Topics名和Items名,先得到Topics名,然后得到Items名。在本 實(shí)例中XTYP_REQUEST選項(xiàng)的Topics名是“query”,Items名為“1”,而XTYP_EXECUTE選項(xiàng)的Topics名是 “data”,Items名為“1”,但I(xiàn)tems名都沒有被利用。
以下是用于客戶程序的主函數(shù),也需要用DdeInitialize()函數(shù)初始化,并設(shè)置過濾類型。其中使用了類型調(diào)用函數(shù)DdeCall()。DdeClient()函數(shù)的代碼如下:
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"));
}
在類型調(diào)用的DdeCall()函數(shù)中,首先獲得Service名、Topics名和Items名的字符串句柄,然后用DdeConnect()函數(shù)與服務(wù)程序連接。如果連接成功,就可以用DdeClientTransaction()
函數(shù)和用XTYP_REQUEST和XTYP_EXECUTE類型向服務(wù)程序發(fā)送數(shù)據(jù)。其中,對(duì)于XTYP_REQUEST,可以用DdeGetData ()函數(shù)從服務(wù)程序獲得數(shù)據(jù)。最后用DdeDisconnect()函數(shù)斷開與服務(wù)程序的連接,并且用DdeFreeStringHandle()函數(shù)釋 放Service名、Topics名和Items名的字符串句柄。DdeCall()函數(shù)的源代碼如下:
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:
???????
??????? // 向服務(wù)器發(fā)送請(qǐng)求
??????? hData = DdeClientTransaction(NULL,0,hConv,
??????????? hszItem, CF_TEXT, iType, 5000, &dwResult);
???????
??????? // 從服務(wù)器取得返回值
??????? dwLength = DdeGetData(hData, (LPBYTE)szBuffer,sizeof(szBuffer), 0);
??????? if (dwLength > 0)
??????????? m_strReply=szBuffer;
??????? break;
??? case XTYP_EXECUTE:
??????? strcpy(szBuffer,m_strRequest);
???????
??????? // 向服務(wù)器發(fā)送執(zhí)行命令
??????? 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);
}
總結(jié)
以上是生活随笔為你收集整理的windows 进程通信(使用DDE)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UNIX 环境高级编程读书笔记(1)
- 下一篇: Windows10更新安装失败,错误0x