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