1. 程式人生 > >MFC基於訊息,使用事件驅動(Message Based,Event Driven)機制

MFC基於訊息,使用事件驅動(Message Based,Event Driven)機制

        MFC程式基於訊息,而使用事件驅動(Message Based,Event Driven)。也就是說MFC就是一個死迴圈,裡面有很多的條件,每個條件對應一個方法。這些條件就是有訊息類定義,當用戶觸發事件時,將傳送訊息到響應的視窗。當程式收到訊息時進行解析,判斷如果符合條件,將運行當前事件的處理方法。

MSG msg;
while(GetMessage(&msg,NULL,NULL,NULL))
 {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
 }
         每一個程式都存在上述的迴圈,而MSG是一個結構,是Windows內設的一種資料格式,可以在WinUser.h中找到,程式碼如下:
/*
  * Message structure
  */
 typedef struct tagMSG {
     HWND        hwnd;
     UINT        message;
     WPARAM      wParam;
     LPARAM      lParam;
     DWORD       time;
     POINT       pt;
 #ifdef _MAC
     DWORD       lPrivate;
#endif
 } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
        接受並處理訊息的主角是視窗,每一個視窗都必須要有能夠處理訊息的方法,稱為“視窗函式”(Window Procedure/Function)。當視窗獲得訊息後,必須判斷訊息的類別,將訊息轉換(TranslateMessage(&msg)轉換鍵盤訊息),然後將訊息傳遞到(DispatchMessage(&msg))視窗函式去處理。
        視窗函式是一個回撥函式(使用者定義的函式用於Windows作業系統呼叫的函式),它的形式如下所示。
LRESULT CALLBACK WinProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
        其中wParam和lParam的意義因訊息的不同而不同,但可以知道的是wParam的位數是隨著作業系統的位數而定的,在32位的作業系統中為32位,當然64位的就為64位。知道了這個函式後,如果要將每一個訊息對應到響應的處理函式中就需要如switch/case結構來判斷,為了讓程式更好的模組化,需要了解Message Map(訊息對映)的原理。

一、訊息對映機制

        訊息系統對於一個win32程式來說十分重要,它是一個程式執行的動力源泉。一個訊息,是系統定義的一個32位的值,他唯一的定義了一個事件,向Windows發出一個通知,告訴應用程式某個事情發生了。例如,單擊滑鼠、改變視窗尺寸、按下鍵盤上的一個鍵都會使Windows傳送一個訊息給應用程式。訊息可以由系統或者應用程式產生。系統在發生輸入事件時產生訊息。舉個例子, 當用戶敲鍵, 移動滑鼠或者單擊控制元件。系統也產生訊息以響應由應用程式帶來的變化, 比如應用程式改變系統字型改變窗體大小。應用程式可以產生訊息使窗體執行任務,或者與其他應用程式中的視窗通訊。 


  在選單中選擇View-->Class Wizard,也可以用單擊滑鼠右鍵,選擇Class Wizard,同樣可以啟用Class Wizard。選擇Message Map標籤,從Class name組合框中選取我們想要新增訊息的類。在Object IDs列表框中,選取類的名稱。此時, Messages列表框顯示該類的大多數(若不是全部的話)可過載成員函式和視窗訊息。類過載顯示在列表的上部,以實際虛構成員函式的大小寫字母來表示。其他為視窗訊息,以大寫字母出現,描述了實際視窗所能響應的訊息ID。選中我們想新增的訊息,單擊Add Function按鈕,Class Wizard自動將該訊息新增進來。

          1、一個MFC訊息響應函式在程式中有三處相關資訊:函式原型,函式實現,關聯訊息和訊息響應函式的巨集。

函式原型——標頭檔案CDrawView——兩個AFX_MSG註釋巨集之間——訊息響應函式原型的宣告 —— afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

函式實現——原始檔CDrawView()——OnLButtonDown(UINT nFlags, CPoint point)

關聯訊息和訊息響應函式的巨集——原始檔CDrawView()——BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP()之間


           2、、 MFC訊息對映機制的具體實現方法是:在每個能接收和處理訊息的類中,定義一個訊息和訊息函式靜態對照表,即:訊息對映表。在訊息對映表中,訊息與對應訊息處理函式指標是成對出現的。某個類能處理的所有訊息及其對應的訊息處理函式的地址都列在這個類所對應的靜態表中。當有訊息需要處理時,程式只要搜尋該訊息靜態表,查看錶中是否含有該訊息,就可知道該類能否處理此訊息。如果能處理該訊息,則同樣依照靜態表能很容易找到並呼叫對應的訊息處理函式。

           3、 MFC訊息對映機制的實際實現過程:MFC在後臺維護了一個視窗控制代碼與對應的C++物件指標的對照表。

           4、MFC建立訊息的步驟如下:訊息的宣告、訊息的對映、訊息的實現

宣告:

//{{AFX_MSG
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

對映:
BEGIN_MESSAGE_MAP(CTestDialog, CDialog)
//{{AFX_MSG_MAP(CTestDialog)
ON_WM_TIMER()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
實現:
void CTestDialog::OnPaint() 
{
}

void CTestDialog::OnTimer(UINT nIDEvent) 
{	
    CDialog::OnTimer(nIDEvent);
}

二、事件驅動機制

         EVENT有兩種狀態:發訊號,不發訊號。 SetEvent/ResetEvent分別將EVENT置為這兩種狀態分別是發訊號與不發訊號。WaitForSingleObject()等待,直到引數所指定的OBJECT成為發訊號狀態時才返回,OBJECT可以是EVENT,也可以是其它核心物件。當你建立一個執行緒時,其實那個執行緒是一個迴圈,不像上面那樣只執行一次的。這樣就帶來了一個問題,在那個死迴圈裡要找到合適的條件退出那個死迴圈,那麼是怎麼樣實現它的呢?在Windows裡往往是採用事件的方式,當然還可以採用其它的方式。在這裡先介紹採用事件的方式來通知從執行緒執行函式退出來,它的實現原理是這樣,在那個死迴圈裡不斷地使用 WaitForSingleObject函式來檢查事件是否滿足,如果滿足就退出執行緒,不滿足就繼續執行。當線上程裡執行阻塞的函式時,就需要在退出執行緒時,先要把阻塞狀態變成非阻塞狀態,比如使用一個執行緒去接收網路資料,同時使用阻塞的SOCKET時,那麼要先關閉SOCKET,再發送事件訊號,才可以退出執行緒的。CreateEvent、SetEvent、WaitForSingleObj

       事件驅動過程需要使用以下三個函式:

      1、CreateEvent

            函功能描述:建立或開啟一個命名的或無名的事件物件.

HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全屬性
BOOL bManualReset, // 復位方法
BOOL bInitialState, // 初始狀態
LPCTSTR lpName // 物件名稱
);

        2. WaitForSingleObject 

        功能描述:用來檢測 hHandle事件的訊號狀態,當函式的執行時間超過dwMilliseconds就返回,但如果引數dwMilliseconds為INFINITE時函式將直到相應時間事件變成有訊號狀態才返回,否則就一直等待下去,直到WaitForSingleObject有返回直才執行後面的程式碼。

DWORD WaitForSingleObject(


  HANDLE hHandle,


  DWORD dwMilliseconds


);
        引數hHandle是一個事件的控制代碼,第二個引數dwMilliseconds是時間間隔。如果時間是有訊號狀態返回WAIT_OBJECT_0,如果時間超過dwMilliseconds值但時間事件還是無訊號狀態則返回WAIT_TIMEOUT。

        hHandle可以是下列物件的控制代碼:
Change notification 、Console input 、Event 、Job 、Memory resource notification、 Mutex 、Process、 Semaphore、 Thread 、Waitable timer等等。

         3、SetEvent

         功能描述:設定事件的狀態為有標記,釋放任意等待執行緒。如果事件是手工的,此事件將保持有標記直到呼叫ResetEvent。這種情況下將釋放多個執行緒,如果事件是自動的,此事件將保持有標記,直到一個執行緒被釋放,系統將設定事件的狀態為無標記。如果沒有執行緒在等待,則此事件將保持有標記,直到一個執行緒被釋放。

         4、ResetEvent

         功能描述:這個函式把指定的事件物件設定為無訊號狀態。

BOOL ResetEvent(
HANDLE hEvent );
        引數說明: 
  hEvent
  [in] 指向事件物件的控制代碼.由 CreateEvent or OpenEvent 函式返回。 這個控制代碼需要擁有EVENT_MODIFY_STATE 訪問許可權.
  函式成功,返回非0值,否則返回0值,可以呼叫GetLastError得到錯誤的詳細資訊。
  注意:
  一個事件物件一直都保持在無訊號狀態,直到顯式呼叫 SetEvent or PulseEvent 函式把它設定到有訊號狀態。 這些無訊號的事件物件會阻塞任何在內部呼叫wait函式的執行緒。 這個函式用於手動重置的事件物件。手動重置的物件線上程釋放後必須手動置為無訊號狀態。 自動重置的事件物件在一個等待它成功的執行緒釋放後會自動變為無訊號狀態。

       5、CloseHandle

       功能描述:使用CloseHandle函式關閉控制代碼。當程序停止時,系統將自動關閉控制代碼。當最後一個控制代碼被關閉後,事件物件將被銷燬。

       6、應用:主要應用是鎖定功能,實現PV操作(PV操作與訊號量的處理相關,P表示通過的意思,V表示釋放的意思,解決程序同步與互斥問題)

       在呼叫的過程中,所有執行緒都可以在一個等待函式中指定事件物件控制代碼。當指定的物件的狀態被置為有訊號狀態時,單物件等待函式將返回。 

       對於多物件等待函式,可以指定為任意或所有指定的物件被置為有訊號狀態。當等待函式返回時,等待執行緒將被釋放去繼續執行。 
    初始狀態在bInitialState引數中進行設定。使用SetEvent函式將事件物件的狀態置為有訊號狀態。使用ResetEvent函式將事件物件的狀態置為無訊號狀態。 
    當一個手動復原的事件物件的狀態被置為有訊號狀態時,該物件狀態將一直保持有訊號狀態,直至明確呼叫ResetEvent函式將其置為無符號狀態。 
    當事件的物件被置為有訊號狀態時,任意數量的等待中執行緒,以及隨後開始等待的執行緒均會被釋放。 
    當一個自動復原的事件物件的狀態被置為有訊號狀態時,該物件狀態將一直保持有訊號狀態,直至一個等待執行緒被釋放;系統將自動將此函式置為無符號狀態。如果沒有等待執行緒正在等待,事件物件的狀態將保持有訊號狀態。 
    多個程序可持有同一個事件物件的多個控制代碼,可以通過使用此物件來實現程序間的同步。下面的物件共享機制是可行的: 
    在CreateEvent函式中,lpEventAttributes引數指定控制代碼可被繼承時,通過CreateProcess函式建立的子程序繼承的事件物件控制代碼。 
    一個程序可以在DuplicateHandle函式中指定事件物件控制代碼,從而獲得一個複製的控制代碼,此控制代碼可以被其它程序使用。 
    一個程序可以在OpenEvent或CreateEvent函式中指定一個名字,從而獲得一個有名的事件物件控制代碼。 
    使用CloseHandle函式關閉控制代碼。當程序停止時,系統將自動關閉控制代碼。當最後一個控制代碼被關閉後,事件物件將被銷燬。

         7、以下是簡單的事件驅動例項:

#include <WINDOWS.H>//需要訪問windows API 函式,因此許加入此標頭檔案
#include <IOSTREAM.H>

DWORD WINAPI Fun1Pro(LPVOID lpParameter);
DWORD WINAPI Fun2Pro(LPVOID lpParameter);

int nTickets = 100;
HANDLE g_hEvent;

void main()//主執行緒
{
	HANDLE hThread1;
	HANDLE hThread2;

//建立人工重置事件物件
// 	g_hEvent = CreateEvent(NULL,FALSE,TRUE,NULL);
// 	SetEvent(g_hEvent);
	g_hEvent = CreateEvent(NULL,FALSE,TRUE,"Tickets");//建立命名事件物件
	if (g_hEvent)
	{
		if (ERROR_ALREADY_EXISTS == GetLastError())
		{
			cout<<"Only one thread can run..."<<endl;
			return;
		}
	}

	hThread1 = CreateThread(NULL,0,Fun1Pro,NULL,0,NULL);
	hThread2 = CreateThread(NULL,0,Fun2Pro,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);

	Sleep(4000);
	CloseHandle(g_hEvent);
}

DWORD WINAPI Fun1Pro(LPVOID lpParameter)
{
	while (TRUE)
	{
		WaitForSingleObject(g_hEvent,INFINITE);
// 		ResetEvent(g_hEvent);
		if (nTickets>0)
		{
			Sleep(1);
			cout<<"Thread1 sells tickets : "<<nTickets--<<endl;
			SetEvent(g_hEvent);
		}
		else
		{
			SetEvent(g_hEvent);
			break;
		}
	}
	return 0;
}

DWORD WINAPI Fun2Pro(LPVOID lpParameter)
{
	while (TRUE)
	{
		WaitForSingleObject(g_hEvent,INFINITE);
// 		ResetEvent(g_hEvent);
		if (nTickets>0)
		{
			Sleep(1);
			cout<<"Thread2 sells tickets : "<<nTickets--<<endl;
			SetEvent(g_hEvent);
		}
		else
		{
			SetEvent(g_hEvent);
			break;
		}
	}
	return 0;
}


相關推薦

MFC基於訊息使用事件驅動Message Based,Event Driven機制

        MFC程式基於訊息,而使用事件驅動(Message Based,Event Driven)。也就是說MFC就是一個死迴圈,裡面有很多的條件,每個條件對應一個方法。這些條件就是有訊息類定義,當用戶觸發事件時,將傳送訊息到響應的視窗。當程式收到訊息時進行解析,判

基於訊息事件驅動的點滴感悟

在入門windows程式設計時,我們總會聽到這麼一句:Message Based,Event Driven。 今天在解決Bug00173911 TL2.6:合併視窗,滑動滑鼠使得右上角的收縮按鈕展開,拖動視窗至最大化,關閉按鈕顯示為還原按鈕 時有點感觸。 在解決該bug後,

基於CNN的影象修復CNN-based Image Inpainting

本文簡單介紹兩篇基於CNN進行影象修復的論文,論文以及原始碼分別為: 兩篇論文的網路框架基本一樣: 主要思路都是結合Encoder-Decoder 網路結構和 GAN (Generative Adversarial Networks),E

微信開發系列_訊息事件的處理

來自微信端的事件可以有多種 1:文字訊息 2:圖片訊息 3:語音訊息 4:點選按鈕事件 5:掃碼事件等等 使用者傳送的訊息或者事件  都是以xml的形式傳送給我們開發者的(也就是伺服器端) 我們也是以xml的格式返回去的 所有首先 我們得把使用者的訊息給獲取並解析了(我

Python web模版Django-25 在Django中載入css例項 基於23對24的index.html進一步修改

      在前面Django-24的筆記中,對index.html用Django-bootstrap3進行了改造簡化,Django-bootstrap3中引用的css,js其實還是Bootstrap的CDN。CDN雖然方便,但以後帶來的問

vue -- 非父子元件傳值事件匯流排eventbus的使用方式

轉自:https://blog.csdn.net/wxl1555/article/details/84646832  一、 前言 先說一下什麼是事件匯流排,其實就是訂閱釋出者模式; 比如有一個bus物件,這個物件上有兩個方法,一個是on(監聽,也就是訂閱),一個是emit(觸發,也

Redis:事件驅動IO多路複用

目錄 §  從Redis的工作模式談起 §  Reactor模式 ·        C10K問題 ·   &nbs

MFC 基於訊息機制的WSAAsyncSelect demo

void CXXXDlg::OnSockEvent(WPARAM wParam,LPARAM lParam) {     SOCKET sSock= static_cast<SOCKET>(wParam);  if(WSAGETSELECTERROR(lParam))      {     

基於SpirngMVC登入攔截器使用了redis快取僅供參考

package com.avic.common.interceptor; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException;

vue v-html動態新增 html 內容渲染事件失效無法從新渲染

內容繫結事件(在新增v-html後面新增程式碼) nextTick 就是節點DOM更新完成後呼叫 ,假如 v-html裡面有一個class=”a”的節點,要在這個DOM上繫結事件,那麼 this

新項目上線河馬體育http://www.hemaj.com-足球即時比分、足球比分、足球比分直播、足球直播

以及 陌生人 足球 get 數據分析 新項目 認識 愛好 體育彩票 河馬體育提供免費足球即時比分、籃球比分、球迷聊天室,為中國體育彩票、競彩彩民以及體育愛好者提供最專業的數據分析、預測。 河馬體育是即時比分和線聊天網站,你可以馬上跟一群互不認識的陌生人聊體育。通過文字逐漸相

jQuery裏面click、this事件遇到Django模型裏for相同的id名和class名想獲取值

相同 技術分享 簡化 不能 png 要求 clas http color 遇到的原型是這樣的!下面我把它簡化一下; click事件: 在瀏覽器裏面只能獲取橫線上面的值,和下面的第一個值!! 這是因為id等級比class高,而且js要求id不能重復! 當jQu

圖像切割—基於圖的圖像切割Graph-Based Image Segmentation

子圖 衡量標準 content 彩色 cep 期待 mean 定義 筆記  圖像切割—基於圖的圖像切割(Graph-Based Image Segmentation) Reference: Efficient Graph-Ba

jQuery批量修改Name值批量增加name後綴非each不需要循環

jquery 批量修改name背景表單字段,配置where屬性自動刷新到可查詢區域,查詢字段需增加_where後綴,響應後臺規則問題批量設置的查詢字段,必須批量增加_where後綴方可正常查詢。each循環有點麻煩,有沒有更方便的批量修改方法。調查方向就是jquery批量修改name,整個互聯網充斥的都是.a

Android 音視頻深入 十六 FFmpeg 推流手機攝像頭實現直播 附源碼下載

音視頻 FFmpeg RTMP 直播 Android 源碼地址https://github.com/979451341/RtmpCamera/tree/master 配置RMTP服務器,雖然之前說了,這裏就直接粘貼過來吧 1.配置RTMP服務器 這個我不多說貼兩個博客分別是在mac和win

Android 音視頻深入 十八 FFmpeg播放視頻有聲音附源碼下載

音視頻 Android FFmpeg 項目地址https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpegv%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91%E6%9C%89%E5%A3%B0%

file_put_contents執行返回falsefile_put_contents falselinux服務器httpd

bsp 關閉selinux lin sys chcon stat CI 返回 sel file_put_contents執行返回false,file_put_contents false(linux服務器httpd) 默認下selinux是開啟的查看SELinux狀

課程一(Neural Networks and Deep Learning)第一週Introduction to Deep Learning—— 0、學習目標

1. Understand the major trends driving the rise of deep learning. 2. Be able to explain how deep learning is applied to supervised learning. 3. Unde

課程一(Neural Networks and Deep Learning)第一週Introduction to Deep Learning—— 2、10個測驗題

1、What does the analogy “AI is the new electricity” refer to?  (B) A. Through the “smart grid”, AI is delivering a new wave of electricity.

天地圖專題七:行政區域標記熱力圖以廣西為例

天地圖方面的文章已很久不寫。 主要是因為上一個專案結束,基本就不用天地圖了。用百度地圖的可能更大一些。   最近上個專案甲方想搞一個類似熱力圖,熱點圖的東西。在天地圖API上沒有找到類似的直接畫熱力圖的介面,只好用了一些替代的方法。 天地圖行政區域,行政區劃直接介面也沒找到,用