亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

? 歡迎來(lái)到蟲(chóng)蟲(chóng)下載站! | ?? 資源下載 ?? 資源專(zhuān)輯 ?? 關(guān)于我們
? 蟲(chóng)蟲(chóng)下載站

?? 新建 文本文檔.txt

?? CRC校驗(yàn) CSerialPort串口類(lèi)C RC校驗(yàn) CSerialPort串口類(lèi)
?? TXT
?? 第 1 頁(yè) / 共 2 頁(yè)
字號(hào):
  
深入探討MFC消息循環(huán)和消息泵
作者:周焱

首先,應(yīng)該清楚MFC的消息循環(huán)(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之間的路由是兩件不同的事情。在MFC的應(yīng)用程序中(應(yīng)用程序類(lèi)基于CWinThread繼承),必須要有一個(gè)消息循環(huán),他的作用是從應(yīng)用程序的消息隊(duì)列中讀取消息,并把它派送出去(::DispatchMessage)。而消息路由是指消息派送出去之后,系統(tǒng)(USER32.DLL)把消息投遞到哪個(gè)窗口,以及以后消息在窗口之間的傳遞是怎樣的。

消息分為隊(duì)列消息(進(jìn)入線(xiàn)程的消息隊(duì)列)和非隊(duì)列消息(不進(jìn)入線(xiàn)程的消息隊(duì)列)。對(duì)于隊(duì)列消息,最常見(jiàn)的是鼠標(biāo)和鍵盤(pán)觸發(fā)的消息,例如WM_MOUSERMOVE,WM_CHAR等消息;還有例如:WM_PAINT、WM_TIMER和WM_QUIT。當(dāng)鼠標(biāo)、鍵盤(pán)事件被觸發(fā)后,相應(yīng)的鼠標(biāo)或鍵盤(pán)驅(qū)動(dòng)程序就會(huì)把這些事件轉(zhuǎn)換成相應(yīng)的消息,然后輸送到系統(tǒng)消息隊(duì)列,由Windows系統(tǒng)負(fù)責(zé)把消息加入到相應(yīng)線(xiàn)程的消息隊(duì)列中,于是就有了消息循環(huán)(從消息隊(duì)列中讀取并派送消息)。還有一種是非隊(duì)列消息,他繞過(guò)系統(tǒng)隊(duì)列和消息隊(duì)列,直接將消息發(fā)送到窗口過(guò)程。例如,當(dāng)用戶(hù)激活一個(gè)窗口系統(tǒng)發(fā)送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。創(chuàng)建窗口時(shí)發(fā)送WM_CREATE消息。在后面你將看到,MS這么設(shè)計(jì)是很有道理的,以及他的整套實(shí)現(xiàn)機(jī)制。

這里講述MFC的消息循環(huán),消息泵。先看看程序啟動(dòng)時(shí),怎么進(jìn)入消息循環(huán)的:
_tWinMain ->AfxWinMain ->AfxWinInit ->CWinThread::InitApplication ->CWinThread::InitInstance ->CWinThread::Run

非對(duì)話(huà)框程序的消息循環(huán)的事情都從這CWinThread的一Run開(kāi)始...

第一部分:非對(duì)話(huà)框程序的消息循環(huán)機(jī)制。

//thrdcore.cpp
// main running routine until thread exits
int CWinThread::Run()
{
 ASSERT_VALID(this);

 // for tracking the idle time state
 BOOL bIdle = TRUE;
 LONG lIdleCount = 0;

 // acquire and dispatch messages until a WM_QUIT message is received.
 for (;;)
 {
  // phase1: check to see if we can do idle work
  while (bIdle &&
   !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
  {
   // call OnIdle while in bIdle state
   if (!OnIdle(lIdleCount++))
    bIdle = FALSE; // assume "no idle" state
  }

  // phase2: pump messages while available
  do
  {
   // pump message, but quit on WM_QUIT
   if (!PumpMessage())
    return ExitInstance();

   // reset "no idle" state after pumping "normal" message
   if (IsIdleMessage(&m_msgCur))
   {
    bIdle = TRUE;
    lIdleCount = 0;
   }

  } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
 }    //無(wú)限循環(huán),退出條件是收到WM_QUIT消息。

 ASSERT(FALSE);  // not reachable
}

這是一個(gè)無(wú)限循環(huán),他的退出條件是收到WM_QUIT消息:

 if (!PumpMessage())
    return ExitInstance();

在PumpMessage中,如果收到WM_QUIT消息,那么返回FALSE,所以ExitInstance()函數(shù)執(zhí)行,跳出循環(huán),返回程序的退出代碼。所以,一個(gè)程序要退出,只用在代碼中調(diào)用函數(shù)

VOID PostQuitMessage( int nExitCode )。指定退出代碼nExitCode就可以退出程序。

下面討論一下這個(gè)函數(shù)Run的流程,分兩步:

1,第一個(gè)內(nèi)循環(huán)phase1。bIdle代表程序是否空閑。他的意思就是,如果程序是空閑并且消息隊(duì)列中沒(méi)有要處理的消息,那么調(diào)用虛函數(shù)OnIdle進(jìn)行空閑處理。在這個(gè)處理中將更新UI界面(比如工具欄按鈕的enable和disable狀態(tài)),刪除臨時(shí)對(duì)象(比如用FromHandle得到的對(duì)象指針。由于這個(gè)原因,在函數(shù)之間傳遞由FromHandle得到的對(duì)象指針是不安全的,因?yàn)樗麤](méi)有持久性)。OnIdle是可以重載的,你可以重載他并返回TRUE使消息循環(huán)繼續(xù)處于空閑狀態(tài)。

NOTE:MS用臨時(shí)對(duì)象是出于效率上的考慮,使內(nèi)存有效利用,并能夠在空閑時(shí)自動(dòng)撤銷(xiāo)資源。關(guān)于由句柄轉(zhuǎn)換成對(duì)象,可以有若干種方法。一般是先申明一個(gè)對(duì)象obj,然后使用obj.Attatch來(lái)和一個(gè)句柄綁定。這樣產(chǎn)生的對(duì)象是永久的,你必須用obj.Detach來(lái)釋放對(duì)象。

2,第二個(gè)內(nèi)循環(huán)phase2。在這個(gè)循環(huán)內(nèi)先啟動(dòng)消息泵(PumpMessage),如果不是WM_QUIT消息,消息泵將消息發(fā)送出去(::DispatchMessage)。消息的目的地是消息結(jié)構(gòu)中的hwnd字段所對(duì)應(yīng)的窗口。
//thrdcore.cpp
BOOL CWinThread::PumpMessage()
{
 ASSERT_VALID(this);
 
 //如果是WM_QUIT就退出函數(shù)(return FALSE),這將導(dǎo)致程序結(jié)束.
 if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) {
#ifdef _DEBUG
  if (afxTraceFlags & traceAppMsg)
   TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");
  m_nDisablePumpCount++; // application must die
   // Note: prevents calling message loop things in 'ExitInstance'
   // will never be decremented
#endif
  return FALSE;
 }

#ifdef _DEBUG
 if (m_nDisablePumpCount != 0)
 {
  TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n");
  ASSERT(FALSE);
 }
#endif

#ifdef _DEBUG
 if (afxTraceFlags & traceAppMsg)
  _AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
#endif

 // process this message

 if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
 {
  ::TranslateMessage(&m_msgCur); //鍵轉(zhuǎn)換
  ::DispatchMessage(&m_msgCur); //派送消息
 }
 return TRUE;
}

在這一步有一個(gè)特別重要的函數(shù)大家一定認(rèn)識(shí):PreTranslateMessage。這個(gè)函數(shù)在::DispatchMessage發(fā)送消息到窗口之前,進(jìn)行對(duì)消息的預(yù)處理。PreTranslateMessage函數(shù)是CWinThread的成員函數(shù),大家重載的時(shí)候都是在View類(lèi)或者主窗口類(lèi)中,那么,它是怎么進(jìn)入別的類(lèi)的呢?代碼如下:
//thrdcore.cpp
BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
 ASSERT_VALID(this);

 // 如果是線(xiàn)程消息,那么將會(huì)調(diào)用線(xiàn)程消息的處理函數(shù)
 if (pMsg->hwnd == NULL && DispatchThreadMessageEx(pMsg))
  return TRUE;

 // walk from target to main window
 CWnd* pMainWnd = AfxGetMainWnd();
 if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
  return TRUE;

 // in case of modeless dialogs, last chance route through main
 //   window's accelerator table
 if (pMainWnd != NULL)
 {
   CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
   if (pWnd->GetTopLevelParent() != pMainWnd)
   return pMainWnd->PreTranslateMessage(pMsg);
 }

 return FALSE;   // no special processing
}

由上面這個(gè)函數(shù)可以看出:

第一,如果(pMsg->hwnd == NULL),說(shuō)明這是一個(gè)線(xiàn)程消息。調(diào)用CWinThread::DispatchThreadMessageEx到消息映射表找到消息入口,然后調(diào)用消息處理函數(shù)。

NOTE: 一般用PostThreadMessage函數(shù)發(fā)送線(xiàn)程之間的消息,他和窗口消息不同,需要指定線(xiàn)程id,消息激被系統(tǒng)放入到目標(biāo)線(xiàn)程的消息隊(duì)列中;用ON_THREAD_MESSAGE( message, memberFxn )宏可以映射線(xiàn)程消息和他的處理函數(shù)。這個(gè)宏必須在應(yīng)用程序類(lèi)(從CWinThread繼承)中,因?yàn)橹挥袘?yīng)用程序類(lèi)才處理線(xiàn)程消息。如果你在別的類(lèi)(比如視圖類(lèi))中用這個(gè)宏,線(xiàn)程消息的消息處理函數(shù)將得不到線(xiàn)程消息。

第二,消息的目標(biāo)窗口的PreTranslateMessage函數(shù)首先得到消息處理權(quán),如果函數(shù)返回FALSE,那么他的父窗口將得到消息的處理權(quán),直到主窗口;如果函數(shù)返回TRUE(表示消息已經(jīng)被處理了),那么就不需要調(diào)用父類(lèi)的PreTranslateMessage函數(shù)。這樣,保證了消息的目標(biāo)窗口以及他的父窗口都可以有機(jī)會(huì)調(diào)用PreTranslateMessage--在消息發(fā)送到窗口之前進(jìn)行預(yù)處理(如果自己處理完然后返回FALSE的話(huà) -_-b),如果你想要消息不傳遞給父類(lèi)進(jìn)行處理的話(huà),返回TRUE就行了。

第三,如果消息的目標(biāo)窗口和主窗口沒(méi)有父子關(guān)系,那么再調(diào)用主窗口的PreTranslateMessage函數(shù)。為什么這樣?由第二步知道,一個(gè)窗口的父窗口不是主窗口的話(huà),盡管它的PreTranslateMessage返回FALSE,主窗口也沒(méi)有機(jī)會(huì)調(diào)用PreTranslateMessage函數(shù)。我們知道,加速鍵的轉(zhuǎn)換一般在框架窗口的PreTranslateMessage函數(shù)中。我找遍了MFC中關(guān)于加速鍵轉(zhuǎn)換的處理,只有CFrameWnd,CMDIFrameWnd,CMDIChildWnd等窗口類(lèi)有。所以,第三步的意思是,如果消息的目標(biāo)窗口(他的父窗口不是主窗口,比如一個(gè)這樣的非模式對(duì)話(huà)框)使消息的預(yù)處理繼續(xù)漫游的話(huà)(他的PreTranslateMessage返回FALSE),那么給一次機(jī)會(huì)給主窗口調(diào)用PreTranslateMessage(萬(wàn)一他是某個(gè)加速鍵消息呢?),這樣能夠保證在有非模式對(duì)話(huà)框的情況下還能保證主窗口的加速鍵好使。
我做了一個(gè)小例子,在對(duì)話(huà)框類(lèi)的PreTranslateMessage中,返回FALSE。在主窗口顯示這個(gè)非模式對(duì)話(huà)框,在對(duì)話(huà)框擁有焦點(diǎn)的時(shí)候,仍然能夠激活主窗口的快捷鍵。

總之,整個(gè)框架就是讓每個(gè)消息的目標(biāo)窗口(包括他的父窗口)都有機(jī)會(huì)參與消息到來(lái)之前的處理。呵呵~

至此,非對(duì)話(huà)框的消息循環(huán)和消息泵的機(jī)制就差不多了。這個(gè)機(jī)制在一個(gè)無(wú)限循環(huán)中,不斷地從消息隊(duì)列中獲取消息,并且保證了程序的線(xiàn)程消息能夠得到機(jī)會(huì)處理,窗口消息在預(yù)處理之后被發(fā)送到相應(yīng)的窗口處理過(guò)程。那么,還有一點(diǎn)疑問(wèn),為什么要一會(huì)兒調(diào)用::PeekMessage,一會(huì)兒調(diào)用::GetMessage呢,他們有什么區(qū)別?

NOTE:一般來(lái)說(shuō),GetMessage被設(shè)計(jì)用來(lái)高效地從消息隊(duì)列獲取消息。如果隊(duì)列中沒(méi)有消息,那么函數(shù)GetMessage將導(dǎo)致線(xiàn)程休眠(讓出CPU時(shí)間)。而PeekMessage是判斷消息隊(duì)列中如果沒(méi)有消息,它馬上返回0,不會(huì)導(dǎo)致線(xiàn)程處于睡眠狀態(tài)。

在上面的phase1第一個(gè)內(nèi)循環(huán)中用到了PeekMessage,它的參數(shù)PM_NOREMOVE表示并不從消息隊(duì)列中移走消息,而是一個(gè)檢測(cè)查詢(xún),如果消息隊(duì)列中沒(méi)有消息他立刻返回0,如果這時(shí)線(xiàn)程空閑的話(huà)將會(huì)引起消息循環(huán)調(diào)用OnIdle處理過(guò)程(上面講到了這個(gè)函數(shù)的重要性)。如果將::PeekMessage改成::GetMessage(***),那么如果消息隊(duì)列中沒(méi)有消息,線(xiàn)程將休眠,直到線(xiàn)程下一次獲得CPU時(shí)間并且有消息出現(xiàn)才可能繼續(xù)執(zhí)行,這樣,消息循環(huán)的空閑時(shí)間沒(méi)有得到應(yīng)用,OnIdle也將得不到執(zhí)行。這就是為什么既要用::PeekMessage(查詢(xún)),又要用::GetMessage(做實(shí)際的工作)的緣故。

第二部分: 對(duì)話(huà)框程序的消息循環(huán)機(jī)制

 

基于對(duì)話(huà)框的MFC工程和上面的消息循環(huán)機(jī)制不一樣。實(shí)際上MFC的對(duì)話(huà)框工程程序就是模式對(duì)話(huà)框。他和上面講到的非對(duì)話(huà)框程序的不同之處,主要在于應(yīng)用程序?qū)ο蟮腎nitInstance()不一樣。

//dlg_5Dlg.cpp
BOOL CDlg_5App::InitInstance()
{
 AfxEnableControlContainer();
#ifdef _AFXDLL
 Enable3dControls();   // Call this when using MFC in a shared DLL
#else
 Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

 CDlg_5Dlg dlg; //定義一個(gè)對(duì)話(huà)框?qū)ο? m_pMainWnd = &dlg;
 int nResponse = dlg.DoModal(); //對(duì)話(huà)框的消息循環(huán)在這里面開(kāi)始
 if (nResponse == IDOK)
 {
  // TODO: Place code here to handle when the dialog is
  //  dismissed with OK
 }
 else if (nResponse == IDCANCEL)
 {
  // TODO: Place code here to handle when the dialog is
  //  dismissed with Cancel
 }

 // Since the dialog has been closed, return FALSE so that we exit the
 //  application, rather than start the application's message pump.
 return FALSE;
}

NOTE: InitInstance函數(shù)返回FALSE,由最上面程序啟動(dòng)流程可以看出,CWinThread::Run是不會(huì)得到執(zhí)行的。也就是說(shuō),上面第一部分說(shuō)的消息循環(huán)在對(duì)話(huà)框中是不能執(zhí)行的。實(shí)際上,對(duì)話(huà)框也有消息循環(huán),她的消息循環(huán)在CDialog::DoModal()虛函數(shù)中的一個(gè)RunModalLoop函數(shù)中。

這個(gè)函數(shù)的實(shí)現(xiàn)體在CWnd類(lèi)中:
int CWnd::RunModalLoop(DWORD dwFlags)
{
 ASSERT(::IsWindow(m_hWnd)); // window must be created

?? 快捷鍵說(shuō)明

復(fù)制代碼 Ctrl + C
搜索代碼 Ctrl + F
全屏模式 F11
切換主題 Ctrl + Shift + D
顯示快捷鍵 ?
增大字號(hào) Ctrl + =
減小字號(hào) Ctrl + -
亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频
中文字幕不卡在线| 欧美精品一区二区三| 久久aⅴ国产欧美74aaa| 国产精品网站一区| 6080午夜不卡| 色综合久久久久综合体| 国产成人av电影| 日韩高清在线观看| 一级日本不卡的影视| 国产亚洲福利社区一区| 欧美精品三级日韩久久| 92精品国产成人观看免费 | 免费人成精品欧美精品| 国产精品高潮呻吟| 久久综合九色综合欧美98| 欧美日韩在线综合| 99久久精品国产毛片| 国产九九视频一区二区三区| 日本最新不卡在线| 亚洲一二三区在线观看| 亚洲人妖av一区二区| 久久久精品中文字幕麻豆发布| 欧美精品 日韩| 欧美日韩三级在线| 色久优优欧美色久优优| 97久久精品人人澡人人爽| 风流少妇一区二区| 国产一区二区精品久久91| 蓝色福利精品导航| 免播放器亚洲一区| 日产精品久久久久久久性色| 天堂一区二区在线免费观看| 亚洲国产日韩一区二区| 亚洲动漫第一页| 亚洲国产成人精品视频| 亚洲综合久久久久| 亚洲一区二区三区视频在线播放| 亚洲色图视频网站| 亚洲日本一区二区三区| 亚洲精品日产精品乱码不卡| 亚洲天天做日日做天天谢日日欢 | 福利电影一区二区| 国产成人av电影在线| 国产高清无密码一区二区三区| 国产一二精品视频| 国产一区二区三区精品视频| 国产精品自在在线| 成人国产精品免费网站| proumb性欧美在线观看| 91欧美一区二区| 色天天综合色天天久久| 欧美卡1卡2卡| 欧美成人免费网站| 欧美激情一区二区三区| 中文字幕精品一区| 亚洲欧美综合在线精品| 一区二区三区国产精品| 日韩av不卡在线观看| 麻豆久久久久久| 国产大片一区二区| 91丨九色丨国产丨porny| 欧美午夜一区二区| 欧美videos大乳护士334| 国产女主播在线一区二区| 国产精品久久久久三级| 亚洲成av人片在www色猫咪| 日本成人在线视频网站| 国产一区二区伦理片| 97久久超碰国产精品| 欧美卡1卡2卡| 国产三级精品视频| 亚洲综合丁香婷婷六月香| 免费的国产精品| 成人黄色777网| 制服丝袜中文字幕一区| 久久人人爽人人爽| 亚洲欧美日韩国产中文在线| 奇米影视7777精品一区二区| 成人高清视频在线| 91精品国产欧美日韩| 中文字幕日韩av资源站| 美腿丝袜在线亚洲一区| av电影天堂一区二区在线| 欧美日韩电影在线播放| 国产亚洲精品资源在线26u| 亚洲人精品一区| 久久99国内精品| 色哟哟精品一区| 久久久一区二区三区捆绑**| 亚洲成av人综合在线观看| 丁香天五香天堂综合| 欧美日韩高清影院| 中文字幕在线观看一区二区| 欧美a级理论片| 91视频在线观看| 欧美精品一区男女天堂| 午夜亚洲福利老司机| 福利一区二区在线| 日韩欧美中文字幕一区| 亚洲一区二区三区四区在线免费观看| 久久99精品一区二区三区三区| 欧美日韩中文一区| 亚洲欧洲日韩女同| 国产一区二区三区四区五区美女| 欧美日韩国产不卡| 一区二区三区欧美视频| 不卡一区二区在线| 精品日韩一区二区三区 | 国产精品69毛片高清亚洲| 欧美精品自拍偷拍| 亚洲人成精品久久久久| 国产成人精品亚洲日本在线桃色 | 精品免费99久久| 亚洲第四色夜色| 色综合咪咪久久| 中文字幕一区二区三区在线观看| 久久99精品国产麻豆不卡| 欧美挠脚心视频网站| 亚洲一区二区在线视频| 99精品视频在线观看免费| 国产嫩草影院久久久久| 狠狠久久亚洲欧美| 日韩一区二区三区电影在线观看| 亚洲综合男人的天堂| 日本电影欧美片| 国产精品电影一区二区| 成人午夜大片免费观看| 久久久久国产精品厨房| 国内精品写真在线观看| 精品国产在天天线2019| 蜜桃视频在线一区| 欧美一级xxx| 玖玖九九国产精品| 精品国产亚洲在线| 国产在线精品一区二区三区不卡| 精品电影一区二区| 狠狠色综合日日| 久久久噜噜噜久久人人看| 国产麻豆精品视频| 久久天天做天天爱综合色| 国产很黄免费观看久久| 国产精品欧美久久久久无广告| 成人免费高清视频在线观看| 国产精品美女www爽爽爽| 波多野结衣中文字幕一区二区三区 | 秋霞国产午夜精品免费视频| 日韩视频在线你懂得| 久久99精品久久久久久动态图 | 中文字幕欧美国产| 9色porny自拍视频一区二区| 一区二区在线看| 欧美日韩在线播放三区四区| 午夜电影久久久| 欧美精品一区二区三| 国产白丝网站精品污在线入口| 国产精品丝袜在线| 色一情一乱一乱一91av| 日韩精品电影一区亚洲| 久久久午夜精品| 色综合中文字幕国产 | 欧美午夜精品电影| 婷婷综合久久一区二区三区| 日韩一级大片在线观看| 国产传媒欧美日韩成人| 亚洲人成在线观看一区二区| 欧美美女一区二区在线观看| 久草这里只有精品视频| 国产精品久线在线观看| 欧美日韩精品一区二区三区蜜桃 | 激情国产一区二区| 1区2区3区国产精品| 欧美日韩国产精品成人| 国产在线精品一区二区夜色 | 欧美精品18+| 国产精品性做久久久久久| 中文字幕佐山爱一区二区免费| 国产一本一道久久香蕉| 一区二区高清免费观看影视大全| 日韩精品一区二区三区在线观看| 99精品视频一区| 精品一区二区久久久| 亚洲视频在线观看三级| 日韩视频中午一区| 97se亚洲国产综合自在线| 另类的小说在线视频另类成人小视频在线 | 亚洲成人免费影院| 精品国产制服丝袜高跟| 在线观看国产一区二区| 国产一区激情在线| 一区二区三区在线免费播放| 精品少妇一区二区三区日产乱码 | 夜夜精品视频一区二区| 久久久综合精品| 欧美日韩精品欧美日韩精品一| 国产成人精品在线看| 日韩av中文在线观看| 成人欧美一区二区三区黑人麻豆| 精品日韩在线观看| 欧美日韩中文字幕一区| 91年精品国产|