天天看點

程式設計高手:VC 6.0下利用消息實作内部程序通訊

引言

  内部程序間通訊和資料交換有消息、共享 記憶體、匿名(命名)管道、郵槽、Windows套接字等多種技術。其中利用消息機制實作IPC雖然同其他方法相比有交換的資料量小、攜帶的資訊少等缺點,但由于其實作友善、應用靈活而廣泛應用于無須大量、頻繁資料交換的内部程序通訊系統之中,尤其是對于在上層主要 軟體與底層工作軟體之間的指令與響應上更能充分顯示其良好的性能。本文就通過編制一個主要軟體和一個受其操作的底層工作軟體來闡述如何用VC++6.0通過消息來實作内部程序 通信。

Windows消息機制

  Windows是一種面向對象的體系結構,Windows環境和應用程式都是通過消息來互動的。Windows應用程式開始執行後,Windows為該程式建立一個“消息隊列(message queue)”,用以存放郵寄給該程式可能建立的各種不同視窗的消息。消息隊列中消息的結構(MSG)為:

typedef struct tagMSG{

 HWND hwnd;

 UINT message;

 WPARAM wParam;

 LPARAM lParam;

 DWORD time;

 POINT pt;

}MSG;

  其中第一個成員變量是用以辨別接收消息的視窗的視窗句柄;第二個參數便是消息辨別号,如WM_PAINT;第三個和第四個參數的具體意義同message值有關,均為消息參數。前四個參數是非常重要和經常用到的,至于後兩個參數則分别表示郵寄消息的時間和光标位置(螢幕坐标)。

  把消息傳送到應用程式有兩種方法:一種是由系統将消息“郵寄(post)”到應用程式的“消息隊列”這是“進隊消息”Win32 API有對應的函數:PostMessage(),此函數不等待該消息處理完就傳回;而另一種則是由系統在直接調用視窗函數時将消息“發送(send)”給應用程式的視窗函數,屬于“不進隊消息”對應的函數是SendMessage()其必須等待該消息處理完後方可傳回。消息是在消息循環中被處理的,下面這段代碼就是一個典型的消息循環:

MSG msg; //定義消息名

while (GetMessage (&msg, NULL, 0, 0))

{

 TranslateMessage (&msg) ; //翻譯消息

 DispatchMessage (&msg) ; //撤去消息

}

return msg.wParam ;

  消息循環以GetMessage調用開始,它從消息隊列中取出一個消息。該函數的四個參數可以有控制地擷取消息,第一個參數指定要接收消息的MSG結構的位址,第二個參數表示視窗句柄,一般将其設定為空,表示要擷取該應用程式建立的所有視窗的消息;第三、四參數用于指定消息範圍。後面三個參數被設定為預設值,用于接收發送到屬于這個應用程式的任何一個視窗的所有消息。在接收到除WM_QUIT之外的任何一個消息後,GetMessage()傳回TRUE;如果GetMessage收到一個WM_QUIT消息,則傳回FALSE以退出消息循環,終止程式運作。是以,在接收到WM_QUIT之前,帶有GetMessage()的消息循環可以一直循環下去。

  當除WM_QUIT的消息用GetMessage讀入後,首先要經過函數TranslateMessage()對其進行解釋,但對大多數消息來說并不起什麼作用。這裡起關鍵作用的是DispatchMessage()函數,把由GetMessage擷取的Windows消息傳送給在MSG結構中為視窗所指定的視窗過程。在消息處理函數處理完消息之後,代碼又循環到開始去接收另一個消息,這樣就完成了一個完整的消息循環。

   主要程式的實作

  本文将設計一個主要程式和一個底層工作程式,由主要程式通過消息來控制底層工作程式的工作狀态。這裡首先對主要程式的設計過程進行介紹:

  首先建立一個單文檔工程,添加三個菜單“指令一”、“指令二”、“指令三”及與之對應的指令響應函數:

OnSendComm1()

{

 CString str="Receiver";

 CWnd *pWnd=CWnd::FindWindow(NULL,str);

 if(pWnd)

  pWnd->SendMessage(WM_COMM,0,0);

} 

OnSendComm2()

{

 CString str="Receiver";

 CWnd *pWnd=CWnd::FindWindow(NULL,str);

 if(pWnd)

  pWnd->SendMessage(WM_COMM,0,1);

}

OnSendComm3()

{

 CString str="Receiver";

 CWnd *pWnd=CWnd::FindWindow(NULL,str);

 if(pWnd)

  pWnd->SendMessage(WM_COMM,1,0);

}

  這裡,WM_COMM是自定義消息,用于在主要程式和底層通信程式之間進行聯系,為了能夠使用該消息,必須首先添加預定義語句:

#define WM_COMM WM_USER + 100

  上述幾個函數的結構沒有什麼差別,首先通過FindWindow()傳回由str變量指定視窗标題的應用程式主視窗句柄并将其儲存到pWnd。然後再通過該句柄調用SendMessage()函數并發送剛才定義的WM_COMM消息及其消息參數。如果函數被正确執行,底層程式将收到來發自主要程式的WM_COMM消息并可在消息響應函數中完成對消息參數的判斷處理以及進一步的操作。

底層工作程式的實作

  最後,來介紹一下底層工作程式的實作過程。底層工作程式也是一個單文檔應用程式(當然,對于多文檔應用程式也是一樣)。由于主要程式是通過對應用程式主視窗标題的捕獲來得到底層程式主視窗句柄的。是以需要在底層工作程式應用類的初始化應用函數InitInstance()中添加(在函數末尾添加)下面語句:

m_pMainWnd->SetWindowText("Receiver");

  以設定底層工作程式的視窗标題。同時還要在主架構類的頭檔案MainFrm.h中添加與主要程式一樣的自定義消息定義:

#define WM_COMM WM_USER+100

  由于底層程式需要接收并響應主要程式發出的通知消息,是以還要在底層工作程式中添加對自定義消息WM_COMM的響應處理:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

//{{AFX_MSG_MAP(CMainFrame)

// NOTE - the ClassWizard will add and remove mapping macros here.

// DO NOT EDIT what you see in these blocks of generated code !

ON_WM_CREATE()

//}}AFX_MSG_MAP

ON_MESSAGE(WM_COMM,OnSendMsg)

END_MESSAGE_MAP()

……

void CMainFrame::OnSendMsg(WPARAM wParam, LPARAM lParam)

{

 if(wParam==0 && lParam==0)

  AfxMessageBox("主要程式發送指令一!");

 if(wParam==0 && lParam==1)

  AfxMessageBox("主要程式發送指令二!");

 if(wParam==1 && lParam==0)

  AfxMessageBox("主要程式發送指令三!");

}

  此後就可以通過辨識消息的兩個消息參數來區分主要程式發送的是哪一個指令進而可以執行相應的操作。執行主要程式和底層工作程式由于本程式采用的是SendMessage()是以當主要程式發送消息給底層工作程式時,底層工作程式彈出響應的模式對話框,在沒有關閉對話框前此消息未處理完,SendMessage()也就沒有執行完,是以主要程式呈阻塞狀态,如改用PoseMessage()則不會發生阻塞,具體選用哪個函數還應根據實際要求靈活掌握。

   結論:

  通過上面的執行個體可以看出利用消息進行程序間通信不失為一種便捷的方法,程序間的資料交換量不大卻能完成相當的功能,上下層次有着明顯的接口,上層和底層隻通過這個接口進行通訊,是以隻要對上下層程式制定好規範詳盡的協定便可編制出協調性很好的軟體控制系統。

繼續閱讀