Visual C++進程間資料通訊的實現http://tech.163.com 2006-01-19 12:18:21 來源: 天極開發 網友評論0 條 論壇
在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會話初始化
使用API函數DdeInitialize(),在DDEML中註冊應用。
會話建立
伺服器:註冊服務DdeNameService.
客戶:串連DdeConnect.
會話過程
類似於Windows的訊息迴圈,會話的過程就是交易處理的過程。客戶通過DdeClientTransaction()來發出事務請求,通過DDE回呼函數,伺服器處理客戶事務請求,返回DdeCreateDataHandle來發送資料,同時客戶可以調用DdeGetData()擷取資料。
會話結束
可由服務方或客戶方來終止會話,推出程式時要注消服務,釋放資源,調用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 2 //定義ITEM的數量;
const char szApp[]="Server"; //server DDE服務名;
const char szTopic[]="Topic";//Server DDE目錄名;
const char *pszItem[NITEM]={"Item1","Item2"};//SERVER ITEM名稱字串數組;
int count=0;//記數,在Static1中顯示;
CString ServerData[NITEM];//存放伺服器中的資料項目內容;
HCONV hConv=0; //交談控制代碼;
DWORD idlnst=0; //DDEML執行個體控制代碼;
HWND hWnd; //視窗控制代碼;
HANDLE hlnst; //執行個體控制代碼;
HSZ hszApp=0; //SERVER服務字串控制代碼;
HSZ hszTopic=0; //SERVER目錄字元串控制代碼;
HSZ hszItem[NITEM]; //Server ITEM字串控制代碼;
BOOL bConnect; // 建立串連標誌;
4、輸入代碼,編譯運行程式。
三、程式碼
//////////////////////////////////////DDE回呼函數;
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://請求串連;
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_ADVREQ:
case XTYP_REQUEST://資料請求;
for(I=0;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;
//建立DDE string
hszApp=DdeCreateStringHandle(idlnst,szApp,0);
hszTopic=DdeCreateStringHandle(idlnst,szTopic,0);
for(int I=0;I hszItem[I]=DdeCreateStringHandle(idlnst,pszItem[I],0);
//註冊服務;
DdeNameService(idlnst,hszApp,0,DNS_REGISTER);
bConnect=FALSE;
SetTimer(1,1000,NULL);//開始定時;
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);
//串連伺服器端;
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);
}
void CDdedemoDlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
KillTimer(1);//銷毀定時;
DdeNameService(idlnst,0,0,DNS_UNREGISTER);//登出服務;
DdeFreeStringHandle(idlnst,hszApp);
DdeFreeStringHandle(idlnst,hszTopic);
for(int I=0;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更新該資料項目;
}
四、小結
Windows提供了很多方法來實現進程之間的通訊,相互傳遞資料,如通過系統剪貼簿方法、共用DLL方法、管道方法等,這些方法的存在保證了程式的健壯性和魯棒性(穩定性),有興趣的讀者可以自行參考有關資料。