MFC框架之OnIdle案例詳解
先看下MSDN對(duì)OnIdle()介紹:
CWinApp::OnIdle
OnIdle is called in the default message loop when the application's message queue is
empty. Use your override to call your own background idle-handler tasks.
對(duì)于一般桌面應(yīng)用程序中比較少重載這個(gè)函數(shù)。對(duì)于像是視頻游戲這一塊確有不少用處。在Win32 SDK的開(kāi)發(fā)環(huán)境中,通過(guò)在消息循環(huán)中添加自已的render()等接口來(lái)使自已的程序核心運(yùn)轉(zhuǎn)起來(lái),這也是常用的一種辦法。來(lái)到MFC的環(huán)境中,保證程序運(yùn)轉(zhuǎn)的核心循環(huán)已經(jīng)被整合到MFC中去了,這時(shí)侯要想將自已的接口函數(shù)可以合理的插進(jìn)MFC的循環(huán)結(jié)構(gòu)中,那么這個(gè)OnIdle()就是一個(gè)非常好的地方,在這兒你可以讓你的代碼獲的足夠的運(yùn)行機(jī)會(huì)。先看看MSDN中對(duì)MFC的程序中Idle狀態(tài)的處理:
對(duì)CWinApp::OnIdle進(jìn)行重載,返回非零值代表還有Idle Task任務(wù)要處理,這樣下次OnIdle()仍然會(huì)繼續(xù)執(zhí)行。在你重載CWinApp::OnIdle()時(shí),不要忘記要先調(diào)用CWinApp::OnIdle()進(jìn)行MFC默認(rèn)處理:
if (CWinApp::OnIdle(lCount)) return TRUE;
如果忘掉了的話,你會(huì)發(fā)現(xiàn)一些MFC的UI會(huì)出現(xiàn)問(wèn)題,比如菜單上的選擇狀態(tài)無(wú)法更新等問(wèn)題。
再下面加上你自已的處理函數(shù)即可:
YourMethod(); return TRUE; // 需要更多次的執(zhí)行。。。
對(duì)于MFC程序來(lái)講,很多是采用MFC的文檔視圖類的框架。比如如果你要讓視圖不斷刷新,在這個(gè)不斷刷新的視圖中可以完成場(chǎng)景渲灑更新等操作。你當(dāng)然可以在 YourMethod()中獲取視圖的pView的指針,然后調(diào)用其內(nèi)的接口函數(shù), 就像這樣:
CMainFrame *parent = (CMainFrame *)AfxGetMainWnd(); if ( parent && parent->GetSafeHwnd() ) { CFrameWnd* pFrame = parent->GetActiveFrame(); CView *pView = pFrame->GetActiveView(); if ( pView ) { pView->Invalidate(); } }
但這會(huì)明顯的讓你的程序和MFC的框架不那么配套,MFC的文檔視圖結(jié)構(gòu)的設(shè)計(jì)思想并沒(méi)有體現(xiàn)出來(lái)。當(dāng)然這樣做也沒(méi)什么錯(cuò)。類似這樣的寫(xiě)法也是可以正常工作的。
如果你查看過(guò)MFC文檔類CDocument的話,你會(huì)發(fā)現(xiàn)它也有一個(gè)虛函數(shù)叫OnIdle(),很明顯這個(gè)函數(shù)就是讓你完成文檔視圖在Idle時(shí)期的處理工作的地方。你完全在其中可以這樣寫(xiě):
POSITION pos = GetFirstViewPosition(); while ( pos != NULL ) { CView* pView = GetNextView( pos ); pView->Invalidate(); pView->UpdateWindow(); }
通過(guò)在文檔的OnIdle中進(jìn)行處理是更合適的地方。但是同樣需要在CWinApp::OnIdle重載函數(shù)中進(jìn)行一些處理:
// In this example, as in most applications, you should let the // base class CWinApp::OnIdle complete its processing before you // attempt any additional idle loop processing. if ( CWinApp::OnIdle(lCount) ) return TRUE; CWinAppEx::OnIdle(0); return TRUE;
你也許會(huì)問(wèn)為什么要加上這句 CWinAppEx::OnIdle(0):加這句的目的其實(shí)我是希望調(diào)用MFC默認(rèn)的對(duì)文檔視圖OnIdle的處理,也就是借用下面一段代碼:
// call doc-template idle hook POSITION pos = NULL; if ( m_pDocManager != NULL ) pos = m_pDocManager->GetFirstDocTemplatePosition(); while ( pos != NULL ) { CDocTemplate* pTemplate = m_pDocManager->GetNextDocTemplate(pos); ASSERT_KINDOF( CDocTemplate, pTemplate ); pTemplate->OnIdle(); }
你完全可以用上面的代碼代替CWinAppEx::OnIdle(0)這句。
至此關(guān)于MFC中OnIdle的使用介紹已經(jīng)完了。很多具體的東西還是需要深入MFC的具體實(shí)現(xiàn)當(dāng)中去看。
CWinThread::Run是程序生命的"活水源頭"(侯捷:《深入淺出MFC》,函數(shù)存在于VC++ 6.0安裝目錄下提供的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)); } ASSERT(FALSE); // not reachable }
首先進(jìn)行PeekMessage()未Peek到并且bIdle為T(mén)rue則進(jìn)行OnIdle()并且lIdleCount++,完成之后返回一個(gè)值,如果要接收更多的空閑處理時(shí)間,則返回非零值,bIdle仍舊為true,繼續(xù)peek,若仍未peek到,則接著OnIdle,此時(shí)的lIdleCout為1,可根據(jù)這個(gè)值進(jìn)行不同優(yōu)先級(jí)的任務(wù)設(shè)置,若peek到了則do,PumpMessage;如果不需要更多的空閑時(shí)間則返回0,bIdle為false,此時(shí)do第二個(gè)循環(huán),主要是lIdleCount置0然后接著peek,下次空閑的時(shí)候?qū)⒅匦逻M(jìn)行OnIdle的任務(wù)。
OnIdle具體如下:
CWinApp::OnIdle virtual BOOL OnIdle( LONG lCount );
返回值:如果要接收更多的空閑處理時(shí)間,則返回非零值;如果不需要更多的空閑時(shí)間則返回0。
參數(shù):
lCount |
該參數(shù)是一個(gè)計(jì)數(shù)值,當(dāng)應(yīng)用程序的消息隊(duì)列為空,OnIdle函數(shù)被調(diào)用時(shí),該計(jì)數(shù)值就增加1。每當(dāng)一條新消息被處理時(shí),該計(jì)數(shù)值就被復(fù)位為0。你可以使用lCount參數(shù)來(lái)確定應(yīng)用程序不處理消息時(shí)空閑時(shí)間的相對(duì)長(zhǎng)度。 |
說(shuō)明:
如果要執(zhí)行空閑時(shí)處理,則重載這個(gè)成員函數(shù)。當(dāng)應(yīng)用程序的消息隊(duì)列為空時(shí),OnIdle就在缺省的消息循環(huán)中被調(diào)用。你可以用重載函數(shù)來(lái)調(diào)用自己的后臺(tái)空閑處理任務(wù)。
OnIdle應(yīng)返回0以表明不需要更多的空閑處理時(shí)間。當(dāng)消息隊(duì)列為空時(shí),OnIdle每被調(diào)用一次lCount參數(shù)就增加,而每處理一條新消息lCount就被復(fù)位為0。你可以根據(jù)這個(gè)計(jì)數(shù)值調(diào)用不同的空閑處理例程。
下面總結(jié)了空閑循環(huán)處理:
1. |
如果微軟基礎(chǔ)類庫(kù)中的消息循環(huán)檢查消息隊(duì)列并發(fā)現(xiàn)沒(méi)有未被處理的消息,它就為應(yīng)用程序?qū)ο笳{(diào)用OnIdle函數(shù),并將lCount參數(shù)設(shè)為0。 |
2. |
OnIdle執(zhí)行一些處理,然后返回一個(gè)非零值,表示它還需要被調(diào)用,以進(jìn)行進(jìn)一步處理。 |
3. |
消息循環(huán)再次檢查消息隊(duì)列。如果沒(méi)有未處理的消息,則再次調(diào)用OnIdle,增加lCount參數(shù)。 |
4. |
最后,OnIdle結(jié)束所有的空閑任務(wù)并返回0。這就告訴消息循環(huán)停止調(diào)用OnIdle直到在消息隊(duì)列中接收到下一條消息為止,在那時(shí),空閑循環(huán)將重新啟動(dòng),而參數(shù)被設(shè)為0。 |
因?yàn)橹挥性贠nIdle返回之后應(yīng)用程序才能處理用戶輸入,因此在OnIdle中不應(yīng)進(jìn)行較長(zhǎng)的任務(wù)。
注意:
OnIdle的缺省實(shí)現(xiàn)更新命令用戶接口對(duì)象,如菜單項(xiàng)和工具條等,還實(shí)現(xiàn)了內(nèi)部數(shù)據(jù)結(jié)構(gòu)的清理。因此,如果你重載了OnIdle,你必須用重載版本中使用的lCount值來(lái)調(diào)用CWinApp::OnIdle。首先調(diào)用所有基類的空閑處理(即直到基類的OnIdle返回0)。如果你需要在基類處理完成之前進(jìn)行一些工作,則應(yīng)回顧基類的實(shí)現(xiàn)以在自己的工作期間選擇一個(gè)合適的lCount值。
示例:
下面的兩個(gè)例子演示了OnIdle的用法。
第一個(gè)例子處理兩個(gè)空閑任務(wù),用lCount參數(shù)來(lái)排列這些任務(wù)的優(yōu)先權(quán)。第一個(gè)任務(wù)優(yōu)先權(quán)較高,一旦可能你就應(yīng)當(dāng)執(zhí)行此任務(wù)。第二個(gè)任務(wù)不十分重要,只有當(dāng)用戶輸入有一個(gè)較長(zhǎng)時(shí)間的間歇的時(shí)候才應(yīng)執(zhí)行此任務(wù)。注意其中對(duì)基類的OnIdle的調(diào)用。第二個(gè)例子管理著一組具有不同優(yōu)先權(quán)的空閑任務(wù)。
BOOL CMyApp::OnIdle(LONG lCount) { BOOL bMore = CWinApp::OnIdle(lCount); if (lCount == 0) { TRACE("App idle for short period of time/n"); bMore = TRUE; } else if (lCount == 10) { TRACE("App idle for longer amount of time/n"); bMore = TRUE; } else if (lCount == 100) { TRACE("App idle for even longer amount of time/n"); bMore = TRUE; } else if (lCount == 1000) { TRACE("App idle for quite a long period of time/n"); // bMore 沒(méi)有被設(shè)為T(mén)RUE, 不在需要空閑 // 重要:bMore 沒(méi)有被設(shè)為 FALSE,因?yàn)?CWinApp::OnIdle可能還有其它空閑任務(wù)要完成。 } return bMore; // 返回TRUE,只要還有其它空閑任務(wù) }
第二個(gè)示例:
// 在這個(gè)例子中,有四個(gè)空閑循環(huán)任務(wù),它們被賦予 // 不同的優(yōu)先權(quán),運(yùn)行的機(jī)會(huì)不同: // Task1在空閑時(shí)總能運(yùn)行,要求在框架處理它自己的空閑循環(huán)任務(wù)時(shí)沒(méi)有消息在等候。(lCount為0或1) // Task2 僅當(dāng)Task1以及運(yùn)行時(shí)才能運(yùn)行,要求當(dāng)Task1運(yùn)行時(shí)沒(méi)有消息在等候。 // Task3和Task4僅當(dāng)Task1和Task2都運(yùn)行之后才能運(yùn)行, // 并且在此期間沒(méi)有消息在等候。如果Task3能夠運(yùn)行, // 則Task4總是在Task3之后立即運(yùn)行。 BOOL CMyApp::OnIdle(LONG lCount) { // 在這個(gè)例子中,像多數(shù)應(yīng)用程序一樣,你應(yīng)該讓基類 // 的CWinApp::OnIdle在你試圖進(jìn)行任何附加的空閑循環(huán) // 過(guò)程之前完成它的處理。 if (CWinApp::OnIdle(lCount)) return TRUE; // 基類的CWinApp::OnIdle為lCount保留0和1給框架自己的 // 空閑處理使用。如果你希望與框架平等地共享空閑處理 // 時(shí)間,則應(yīng)替換上面的if語(yǔ)句,直接調(diào)用CWinApp::OnIdle, // 然后為lCount的值0和/或1加入一個(gè)case語(yǔ)句。首先應(yīng)當(dāng)研 // 究基類的實(shí)現(xiàn)以理解你的空閑循環(huán)任務(wù)將會(huì)如何與框架的 // 空閑循環(huán)處理競(jìng)爭(zhēng)。 switch (lCount) { case 2: Task1(); return TRUE; // 下一次給 Task2 一個(gè)機(jī)會(huì) case 3: Task2(); return TRUE; // 下一次給Task3和Task4一個(gè)機(jī)會(huì) case 4: Task3(); Task4(); return FALSE; // 再次回到空閑循環(huán)任務(wù) } return FALSE; }
到此這篇關(guān)于MFC框架之OnIdle案例詳解的文章就介紹到這了,更多相關(guān)MFC框架之OnIdle內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談C++/C關(guān)于#define的那些奇奇怪怪的用法
本文主要介紹了C++/C關(guān)于#define的那些奇奇怪怪的用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07VC++文件監(jiān)控之FindFirstChangeNotification
因?yàn)镽eadDirectoryChangesW 上次測(cè)試發(fā)現(xiàn)不能多級(jí)目錄監(jiān)控,所以嘗試用FindFirstChangeNotification來(lái)實(shí)施文件監(jiān)控,需要的朋友可以參考下2019-04-04C語(yǔ)言實(shí)現(xiàn)自定義掃雷游戲(遞歸版)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)自定義掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Qt實(shí)現(xiàn)SqlRelationalTable關(guān)聯(lián)表組件
在Qt中我們可以通過(guò)拖拽的方式將不同組件放到指定的位置,實(shí)現(xiàn)圖形化開(kāi)發(fā)極大的方便了開(kāi)發(fā)效率,本章將重點(diǎn)介紹SqlRelationalTable關(guān)聯(lián)表組件的常用方法及靈活運(yùn)用,感興趣的可以了解一下2023-12-12Qt使用QJson模塊實(shí)現(xiàn)解析Json文件
在項(xiàng)目開(kāi)發(fā)過(guò)程中,經(jīng)常會(huì)遇到讀寫(xiě)Json文件的需求,掌握J(rèn)son文件的操作是基礎(chǔ)中的基礎(chǔ),下面我們就來(lái)看看如何使用QT內(nèi)置的QJson模塊解析Json文件吧2023-10-10