定時機制是指在程序運行當中間隔特定的時間引發指定的事件。在DOS下編程時,主要依靠時鐘中斷Int 8及其調用中斷 Int 1cH來實現,應用程序通過修改這些系統中斷來達到實現定時觸發。而在Windows下,若想象在DOS下肆無忌憚的修改系統是不現實的,那么應當如何實現定時機制呢?下面在下就在學習當中的幾點體會談談這個問題,提出幾種方案供大家參考。
第一種方案是大家熟悉的截獲定時消息的途徑。在Windows提供給我們使用的系統資源當中,有一種稱為“定時器(Timer)”的特殊資源,在申請了這類資源的程序當中每間隔一段時間會接收到值為WM_TIMER的消息。需要定時執行的代碼可以放在該消息的處理部分。如果在VC中,我們可以具體按照以下步驟實現這一目的:
- 利用MFC AppWizard創建一個標準的工程,接受所有缺省選項。名為s1
- 在Classview中選中“CMainFrame”類,然后按Ctrl+W激活ClassWizard,在“Message Map”選項卡中Class Name選“CMainFrame”,接著在“Message”中選“WM_TIMER”,最后按下“Add Funcation”。以上步驟加入了對WM_TIMER消息的映射處理。
- 回到Classview中,雙擊“OnCreate”成員函數,在函數的末尾添加申請Timer的語句:
SetTimer(100,1000,NULL);//申請一個標識值為100的Timer,定時間隔為1000毫秒(1秒)。
- 在“Classview”中雙擊OnTimer函數,輸入要定時實現的代碼。本例子中為:
MessageBeep(1000);;//每隔一秒發出通告聲
- 編譯并執行之,我們可以每隔一秒就聽到聲音。這正是我們在OnTimer函數內要求執行的。
實際當中,我們可以將“MessageBeep(1000);”換成任何我們想完成的任務,譬如定時存盤等。
第二種方案也利用Timer資源,但卻是采用已經編寫好的代碼��我們可以加入一個具有定時功能的組件至當前工程當中。這種方法特別適用于基于對話框的工程。具體步驟如下:
- 利用MFC AppWizard創建一個基于對話框的工程,其余接受所有缺省選項。名為s2。
- 在ResourceView中,雙擊IDD_S2_DIALOG,顯示對話框,將其中的“To do:”改為“定時觸發演示的例子”,表明工程的作用。
- 右擊對話框編輯區,在彈出的右鍵菜單中選擇“Insert ActiveX Control”,從彈出的列表框中選擇“Timer Object”,確定后會在對話框內出現一個Timer對象。
- 我們右擊Timer對象,從彈出的菜單中選擇“Properties”,接著選“All”選項卡,將其中的Interval值設為5000,即每隔5秒發生一次Timer事件。
- 回到對話框編輯界面,雙擊Timer,產生一個CS2Dlg::OnTimerTimer1成員函數,接受缺省值,并在函數實現部分輸入:
MessageBox("定時觸發消息框","定時演示" ,MB_OK);
- 編譯并運行此工程,將會在產生的對話框運行期間,每隔5秒彈出一個消息框。
同樣,我們可以以任何自己的代碼來替換5中的消息框語句。詳細見附例s2。
第三種方法是采用線程技術。眾所周知,Windows 9X是一個基于多線程的多任務操作系統,在內核中以線程作為調度的基本單位,由系統分時間片進行調度。利用這一點,我們可以在程序當中創建一個“司職”計時的線程,通過線程間的同步來定時觸發我們要完成的任務的代碼。不象前兩種方法需要至少有一個窗口作為接受消息的主窗口,采用線程技術實現定時觸發將免去創建窗口的麻煩以及帶來的系統各種資源的消耗。下面我們來舉一個例子來說明這個問題:我們在CmyApp類的Initstance成員中不建立主窗口而是創建一個工作線程,該線程休眠一定的時間后,自動調用主線程的SomeThing函數。為了支持線程的運行,我們需要給CmyApp類增加相應的線程函數。下面,我們還是一步一步的實現:
- 利用MFC AppWizard創建一個標準工程,其中為不產生多余的代碼,不選文檔/視圖支持,并選擇單文檔。工程名為S3。
- 在CS3App:: InitInstance()中用“/* … */”注釋掉“return TRUE;”之前的所有代碼。這是為了不建立窗口。并添加以下代碼:
ExitFlag=TRUE;//是否結束主線程的循環的標志變量。因為子線程嚴重依賴主線程,所以在本例子中為了避免沒有主窗口而提前結束應用程序,從而使子線程無法存在,所以給主線程一個循環,知道全局變量ExitFlag在子線程退出前被設置成FALSE為止.
StartThread();//啟動線程
do{}while(ExitFlag);//直到結束子線程
::MessageBox(NULL,"主線程結束!","定時觸發演示",MB_OK);
return TRUE;
- 在Globals中增加一標志變量“ExitFlag”,類型為BOOL。它被主線程用來判斷是否結束自身運行。
- 通過ClassView在CS3App的Public部分聲明以下函數:
void StartThread(void); //啟動線程
static UINT ThreadFunction(void); //主要執行代碼的函數
static UINT StaticThreadFunc(LPVOID lpparam);//設置線程時用到的函數
需要特別指出的是,用AfxBeginThread進行線程設置時,第一參數必須象本例所指出的那樣聲明為Static ,不然參數轉換的錯誤會擾得你不得安寧。
- 在StartThread中輸入如下代碼:
AfxBeginThread(StaticThreadFunc,this);//建立并啟動線程
- 在StaticThreadFunc中輸入如下代碼:
return ThreadFunc();//調用完成主要線程代碼的函數,注意一定要是Static.
- 實現ThreadFunction:
int i;
i=5;//觸發5次
while(i--)
{
Sleep(5000);//間隔5秒
::MessageBox (NULL,"我被定時觸發了!","定時觸發演示",MB_OK);
}
ExitFlag=FALSE;//ExitFlag是一全局變量,通知主線程結束運行。
return 0;
}
- 編譯并運行工程,將看不到應用程序窗口,但可以看到每隔5秒,桌面上出現一個消息框,5次后彈出主線程結束的消息框。
以上即本人在學習當中解決 Windows下實現定時觸發而采取的一些辦法,各自方法的特點也在介紹當中指出。希望所述能給大家一點幫助,更希望能得到大家的指正。如果您有什么意見和設想,歡迎發E-Mail給我(yangshanhe@21cn.com)。
?
==
很早之前2000年的拙作,集在一起,免得自己都不清楚干過什么。