Visual C++進(jìn)程間數(shù)據(jù)通信的實(shí)現(xiàn)
2009-06-08 17:13:17 閱讀(45)
在Windows系統(tǒng)中,各個(gè)應(yīng)用程序(進(jìn)程)之間常常需要交換、傳遞數(shù)據(jù),這就要解決進(jìn)程間的數(shù)據(jù)通信問題。在最初的16位Windows3.x系統(tǒng)中,所有Windows應(yīng)用程序共享單一地址,任何進(jìn)程都能夠?qū)@一共享地址空間的數(shù)據(jù)進(jìn)行讀寫操作。隨著Windwos98、WindowsNT、Windows2000等32位的操作系統(tǒng)的出現(xiàn),規(guī)定每個(gè)進(jìn)程都有自己的地址空間,一個(gè)Windows進(jìn)程不能存取另一個(gè)進(jìn)程的私有數(shù)據(jù),也就是說,雖然兩個(gè)進(jìn)程可以用具有相同值的指針尋址,但所讀寫的只是它們各自的數(shù)據(jù),這樣就減少了進(jìn)程之間的相互干擾。那么上述技術(shù)的采用是否意味著各個(gè)應(yīng)用程序之間不能進(jìn)行數(shù)據(jù)交換了呢?答案當(dāng)然是否定的,強(qiáng)大的Windows系統(tǒng)早已為我們?cè)O(shè)計(jì)了很多方案來解決進(jìn)行間的通信問題,這里我們只探討如何通過動(dòng)態(tài)數(shù)據(jù)交換(DDE)方法實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)通信。
本實(shí)例程序功能如下,服務(wù)器端有兩個(gè)數(shù)據(jù)項(xiàng),一個(gè)是輸入的字符串,另一個(gè)是定時(shí)增加的整數(shù)。運(yùn)行該程序的兩個(gè)實(shí)例后,兩個(gè)程序就可以建立DDE連接,實(shí)現(xiàn)數(shù)據(jù)的傳遞,并將另外一個(gè)實(shí)例傳送過來的數(shù)據(jù)顯示出來。下圖為程序編譯運(yùn)行后的效果圖:
圖一、DDE方法實(shí)現(xiàn)進(jìn)程間數(shù)據(jù)通信程序的界面效果圖
一、實(shí)現(xiàn)方法
自從微軟推出Windows操作系統(tǒng)以來,動(dòng)態(tài)數(shù)據(jù)交換(DDE)就已經(jīng)成為Windows的部分,并且很多Windwos應(yīng)用程序都使用了DDE技術(shù)來實(shí)現(xiàn)進(jìn)程之間的數(shù)據(jù)交換。DDE是建立在Windows內(nèi)部消息系統(tǒng)、全局和共享全局內(nèi)存基礎(chǔ)上的一種協(xié)議,用來協(xié)調(diào)Windows應(yīng)用程序之間的數(shù)據(jù)交換和命令調(diào)用,它已經(jīng)成為應(yīng)用程序之間通信的一種常用方法。
DDE應(yīng)用程序可以分為四種類型:客戶類型、服務(wù)器類型、客戶/服務(wù)器類型和監(jiān)視器。DDE會(huì)話發(fā)生在客戶應(yīng)用程序和服務(wù)器應(yīng)用程序之間??蛻魬?yīng)用程序從服務(wù)器應(yīng)用程序請(qǐng)求數(shù)據(jù)或服務(wù),服務(wù)器應(yīng)用程序響應(yīng)客戶應(yīng)用程序的數(shù)據(jù)或服務(wù)請(qǐng)求??蛻?服務(wù)器應(yīng)用程序是既可以發(fā)出請(qǐng)求,又可以提供信息,監(jiān)視器應(yīng)用程序則是用語調(diào)試的目的。
DDE協(xié)議使用三級(jí)樹型命名:服務(wù)(SERVICE)、主題(TOPIC)和數(shù)據(jù)項(xiàng)(ITEM)來標(biāo)識(shí)DDE所要傳送的數(shù)據(jù)單元。服務(wù)使應(yīng)用程序具有了提供給其他程序的數(shù)據(jù)交換能力;主題類似于目錄,是建立會(huì)話連接的參數(shù):ITEM才是DDE具體通信時(shí)要傳送的數(shù)據(jù)內(nèi)容,比如一個(gè)數(shù)據(jù)或一個(gè)字符串。
動(dòng)態(tài)交換管理庫(DDEML)提供了DDE和應(yīng)用程序級(jí)協(xié)議。使用DDEML開發(fā)的應(yīng)用程序無論是在運(yùn)行一致性方面還是在應(yīng)用程序相互通信方面性能均優(yōu)于沒有使用DDEML的應(yīng)用程序。而且DDEML的應(yīng)用使得開發(fā)支持DDE的應(yīng)用程序容易了許多。
建立DDE會(huì)話后,客戶程序和服務(wù)器程序可以通過三種鏈接方式進(jìn)行數(shù)據(jù)交換,分別是:1、冷鏈接:客戶程序申請(qǐng)數(shù)據(jù),服務(wù)器程序立即給客戶程序發(fā)送數(shù)據(jù);2、溫鏈接:服務(wù)器程序通知客戶程序數(shù)據(jù)數(shù)據(jù)項(xiàng)發(fā)生了改變,但是并沒有將已發(fā)生的值發(fā)送給客戶程序。3、熱鏈接:當(dāng)數(shù)據(jù)項(xiàng)發(fā)生變化時(shí),服務(wù)器程序立即把變化后的值發(fā)送給客戶程序,這是最常用、最方便的方法,下面的例子就使用的這種方法。
DDE會(huì)話初始化
使用API函數(shù)DdeInitialize(),在DDEML中注冊(cè)應(yīng)用。
會(huì)話建立
服務(wù)器:注冊(cè)服務(wù)DdeNameService.
客戶:連接DdeConnect.
會(huì)話過程
類似于Windows的消息循環(huán),會(huì)話的過程就是事務(wù)處理的過程。客戶通過DdeClientTransaction()來發(fā)出事務(wù)請(qǐng)求,通過DDE回調(diào)函數(shù),服務(wù)器處理客戶事務(wù)請(qǐng)求,返回DdeCreateDataHandle來發(fā)送數(shù)據(jù),同時(shí)客戶可以調(diào)用DdeGetData()獲取數(shù)據(jù)。
會(huì)話結(jié)束
可由服務(wù)方或客戶方來終止會(huì)話,推出程序時(shí)要注消服務(wù),釋放資源,調(diào)用DdeUninitialize()。
二、編程步驟
1、啟動(dòng)Visual C++6.0,新建一個(gè)基于對(duì)話框的MFC應(yīng)用程序,取名為DDEdemo,添加兩個(gè)Group Box控件并分別在其上放置編輯控件IDC_EDIT、靜態(tài)控件ID_STATIC1、ID_STATIC2、 ID_STATIC3,用Wizard添加對(duì)應(yīng)成員變m_edit(CString類型),添加并將其Caption置空,最后的界面如圖一所示;
2、使用CLASSWIZARD添加對(duì)話框函數(shù),分別為WM_DESTORY、WM_INITDIALOG、WM_TIMER及IDC_EDIT的EN_CHANGE消息建立對(duì)應(yīng)函數(shù);
3、在DDEdemoDlg.CPP中加入#include "ddel.h"以使用DDEML函數(shù)。并添加以下宏定義和全局變量:
#define NITEM 2 //定義ITEM的數(shù)量;
const char szApp[]="Server"; //server DDE服務(wù)名;
const char szTopic[]="Topic";//Server DDE目錄名;
const char *pszItem[NITEM]={"Item1","Item2"};//SERVER ITEM名稱字符串?dāng)?shù)組;
int count=0;//記數(shù),在Static1中顯示;
CString ServerData[NITEM];//存放服務(wù)器中的數(shù)據(jù)項(xiàng)內(nèi)容;
HCONV hConv=0; //會(huì)話句柄;
DWORD idlnst=0; //DDEML實(shí)例句柄;
HWND hWnd; //窗口句柄;
HANDLE hlnst; //實(shí)例句柄;
HSZ hszApp=0; //SERVER服務(wù)字符串句柄;
HSZ hszTopic=0; //SERVER目錄字符串句柄;
HSZ hszItem[NITEM]; //Server ITEM字符串句柄;
BOOL bConnect; // 建立連接標(biāo)志;
4、輸入代碼,編譯運(yùn)行程序。
三、程序代碼
//////////////////////////////////////DDE回調(diào)函數(shù);
HDDEDATA CALLBACK DdeCallback(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://請(qǐng)求連接;
return ((HDDEDATA)TRUE);//允許;
case XTYP_ADVDATA: //有數(shù)據(jù)到來;
for(I=0;I<NITEM;I++)
if(Item==hszItem[I])
{
DdeGetData(hData,(PBYTE)tmp,255,0);//取得數(shù)據(jù);
switch(I)
{
case 0:
SetDlgItemText(hWnd,IDC_STATIC2,tmp);
break;
case 1:
SetDlgItemText(hWnd,IDC_STATIC3,tmp);
break;
}
}
return ((HDDEDATA)DDE_FACK);//回執(zhí);
case XTYP_ADVREQ:
case XTYP_REQUEST://數(shù)據(jù)請(qǐng)求;
for(I=0;I<NITEM;I++)
if(Item==hszItem[I])
return(DdeCreateDataHandle(idlnst,(PBYTE)(LPCTSTR)ServerData[I],
ServerData[I].GetLength()+1,0,Item,wFmt,0));
}
return(0);
}
///////////////////////////////////////////////////// CddedemoDlg.cpp
CDdedemoDlg::CDdedemoDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDdedemoDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CDdedemoDlg)
m_edit = _T("");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CDdedemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDdedemoDlg)
DDX_Text(pDX, IDC_EDIT1, m_edit);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CDdedemoDlg, CDialog)
//{{AFX_MSG_MAP(CDdedemoDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_WM_DESTROY()
ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//////////////////////////////////////////CDdedemoDlg message handlers
BOOL CDdedemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
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(&idlnst,(PFNCALLBACK)DdeCallback,APPCMD_FILTERINITS|
CBF_FAIL_EXECUTES|CBF_SKIP_CONNECT_CONFIRMS|CBF_FAIL_SELFCONNECTIONS|
CBF_FAIL_POKES,0))
{
MessageBox("DDE SERVER初始化失敗!");
return FALSE;
}
hlnst=AfxGetApp()->m_hInstance;
//創(chuàng)建DDE string
hszApp=DdeCreateStringHandle(idlnst,szApp,0);
hszTopic=DdeCreateStringHandle(idlnst,szTopic,0);
for(int I=0;I<NITEM;I++)
hszItem[I]=DdeCreateStringHandle(idlnst,pszItem[I],0);
//注冊(cè)服務(wù);
DdeNameService(idlnst,hszApp,0,DNS_REGISTER);
bConnect=FALSE;
SetTimer(1,1000,NULL);//開始定時(shí);
return TRUE; // return TRUE unless you set the focus to a control
}
void CDdedemoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CDdedemoDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CDdedemoDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CDdedemoDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
count++;
ServerData[1].Format("%d",count);
SetDlgItemText(IDC_STATIC1,ServerData[1]);
DdePostAdvise(idlnst,hszTopic,hszItem[1]);//通知更新;
if(!bConnect)//如果沒有建立連接
{
hConv=DdeConnect(idlnst,hszApp,hszTopic,NULL);
//連接服務(wù)器端;
if(hConv) //如果建立成功
{
DWORD dwResult;
bConnect=TRUE;
for(int I=0;I<NITEM;I++)
DdeClientTransaction(NULL,0,hConv,hszItem[I],CF_TEXT,XTYP_ADVSTART,
TIMEOUT_ASYNC,&dwResult);
}
}
CDialog::OnTimer(nIDEvent);
}
void CDdedemoDlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
KillTimer(1);//銷毀定時(shí);
DdeNameService(idlnst,0,0,DNS_UNREGISTER);//注銷服務(wù);
DdeFreeStringHandle(idlnst,hszApp);
DdeFreeStringHandle(idlnst,hszTopic);
for(int I=0;I<NITEM;I++)
DdeFreeStringHandle(idlnst,hszItem[I]);
DdeUninitialize(idlnst);
}
void CDdedemoDlg::OnChangeEdit1()
{
// TODO: If this is a RICHEDIT control, the control will not
// send this notification unless you override the CDialog::OnInitDialog()
// function and call CRichEditCtrl().SetEventMask()
// with the ENM_CHANGE flag ORed into the mask.
// TODO: Add your control notification handler code here
UpdateData();
ServerData[0]=m_edit;
DdePostAdvise(idlnst,hszTopic,hszItem[0]); //通知DDE更新該數(shù)據(jù)項(xiàng)目;
}
四、小結(jié)
Windows提供了很多方法來實(shí)現(xiàn)進(jìn)程之間的通信,相互傳遞數(shù)據(jù),如通過系統(tǒng)剪貼板方法、共享DLL方法、管道方法等,這些方法的存在保證了程序的健壯性和魯棒性(穩(wěn)定性),有興趣的讀者可以自行參考有關(guān)資料。