每個(gè)應(yīng)用程序都有個(gè)主函數(shù),在WINDOWS下,只支持兩種類型的應(yīng)用程序——CUI(控制臺(tái)應(yīng)用程序)和GUI(圖形界面應(yīng)用程序),相應(yīng)的,其主函數(shù)類型不同。來(lái)看下這幾個(gè)入口函數(shù)
int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE,PSTR pszCmdLine, int nCmdShow); int WINAPT wWinMain(HINSTANCE hinstExe,HINSTANCE,PWSTR pszCmdLine,int nCmdShow); int __cdecl main(int argc,char *argv[],char *envp[]); int _cdecl wmain(int argc, wchar_t *argv[],wchar_t *envp[]);
前兩個(gè)為GUI的入口函數(shù),后兩個(gè)為CUI的入口函數(shù);事實(shí)上,在一個(gè)進(jìn)程開始運(yùn)行時(shí),WINDOWS OS并不直接從主函數(shù)開始執(zhí)行,而是從另外
一個(gè)比較大的運(yùn)行期啟動(dòng)函數(shù)開始執(zhí)行,不同的入口函數(shù)對(duì)應(yīng)的啟動(dòng)函數(shù)不同:
應(yīng)用程序類型 | 進(jìn)入點(diǎn) | 嵌入可執(zhí)行文件的啟動(dòng)函數(shù) |
需要ANSI字符和字符串的GUI應(yīng)用程序 | WinMain | WinMainCRTStartup |
需要Unicode字符和字符串的GUI應(yīng)用程序 | wWinMainw | WinMainCRTStartup |
需要ANSI字符和字符串的CUI應(yīng)用程序 | main | mainCRTStartup |
需要Unicode字符和字符串的CUI應(yīng)用程序 | wmain | wmainCRTStartup |
啟動(dòng)函數(shù)負(fù)責(zé)對(duì)應(yīng)用程序運(yùn)行前期的初始化,如全局變量的內(nèi)存分配等。如果編寫了一個(gè)wWinMain()函數(shù),以下就是它的調(diào)用過(guò)程: GetStartupInfo(&StartupInfo); int nMainRetVal = wWinMain(GetMjduleHandle(NULL), NULL, pszCommandLineUnicode, (StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ? StartupInfo.wShowWindow:SW_SHOWDEFAULT); ...... exit(0)
當(dāng)入口函數(shù)(主函數(shù))返回時(shí),運(yùn)行期啟動(dòng)函數(shù)就執(zhí)行EXIT函數(shù),此函數(shù)主要完成全局對(duì)象和變量的內(nèi)存釋放任務(wù),之后:再調(diào)用ExitProcess
函數(shù)進(jìn)行撤銷進(jìn)程。即:exit()函數(shù)內(nèi)部調(diào)用了ExitProcess函數(shù)。通常來(lái)說(shuō),這是最完美的進(jìn)程執(zhí)行過(guò)程。由此可以看出exit()函數(shù)原型:進(jìn)行
全局變量和對(duì)象的析構(gòu),然后調(diào)用ExitProcess函數(shù)。注意:它只析構(gòu)全局對(duì)象和變量,而不析構(gòu)局部變量,后面我會(huì)列出具體事例程序來(lái)說(shuō)明。
ExitProcess()函數(shù)實(shí)際上只是用來(lái)進(jìn)行結(jié)束進(jìn)程,如果其后面還有我們預(yù)期要執(zhí)行的代碼,實(shí)際上全未執(zhí)行,這個(gè)性質(zhì)暴露出ExitProcess函數(shù)
的缺陷:可能導(dǎo)致已經(jīng)創(chuàng)建的對(duì)象沒(méi)有析構(gòu)而退出,從而導(dǎo)致內(nèi)存泄露。
TerminateProcess()函數(shù)的實(shí)際作用跟ExitProcess函數(shù)差不多,只不過(guò),此函數(shù)可用來(lái)終止當(dāng)前進(jìn)程之外的另外一個(gè)其它進(jìn)程,同樣的,它
也會(huì)導(dǎo)致和ExitProcess一樣的結(jié)果:內(nèi)存泄露。
如果我們?cè)诰帉憫?yīng)用程序時(shí),打算終止當(dāng)前進(jìn)程,我們?cè)撜{(diào)用哪個(gè)函數(shù)?答案是:三者其實(shí)都一樣! 因?yàn)槿叨伎赡軐?dǎo)致內(nèi)存泄露,但我們擔(dān)心
的過(guò)多了,因?yàn)檫M(jìn)程在結(jié)束時(shí),即使有ExitProcess,TerminateProcess,以及exit函數(shù)調(diào)用而導(dǎo)致的內(nèi)存泄露,OS也會(huì)進(jìn)行清理工作,能保證
我們泄露的內(nèi)存最終被還回到OS中去,而不必?fù)?dān)心當(dāng)前進(jìn)程已經(jīng)退出而導(dǎo)致內(nèi)存泄露,致使其它進(jìn)程無(wú)法使用該內(nèi)存塊。一個(gè)進(jìn)程無(wú)論在什么情
況下終止,都會(huì)進(jìn)行如下工作:
1) 進(jìn)程指定的所有用戶對(duì)象和G D I對(duì)象均被釋放,所有內(nèi)核對(duì)象均被關(guān)閉(如果沒(méi)有其他 進(jìn)程打開它們的句柄,那么這些內(nèi)核對(duì)象將被撤消。
但是,如果其他進(jìn)程打開了它們的句柄, 內(nèi)核對(duì)象將不會(huì)撤消)。
2) 進(jìn)程的退出代碼將從S T I L L _ A C T I V E改為傳遞給E x i t P r o c e s s或Te r m i n a t e P r o c e s s的代碼。
3) 進(jìn)程內(nèi)核對(duì)象的狀態(tài)變成收到通知的狀態(tài)(關(guān)于傳送通知的詳細(xì)說(shuō)明,參見(jiàn)第9章)。系 統(tǒng)中的其他線程可以掛起,直到進(jìn)程終止運(yùn)行。
4) 進(jìn)程內(nèi)核對(duì)象的使用計(jì)數(shù)遞減1。
進(jìn)程只是提供了一段地址空間和內(nèi)核對(duì)象,其運(yùn)行時(shí)通過(guò)其他地址空間內(nèi)的主線程來(lái)體現(xiàn)的。當(dāng)主線程的進(jìn)入點(diǎn)函數(shù)返回時(shí),進(jìn)程也就隨之而技術(shù)。這種進(jìn)程的種植方式是進(jìn)程的正常退出。進(jìn)程中的所有縣城資源都能夠得到正確的清除。除了這種進(jìn)程的正常退出方式之外,優(yōu)勢(shì)還需要在程序中通過(guò)代碼來(lái)強(qiáng)制結(jié)束本進(jìn)程或其他進(jìn)程的運(yùn)行。
ExitProcess
void ExitProcess(UINT uExitCode);
其參數(shù)uExitCode為進(jìn)城設(shè)置了退出代碼。該函數(shù)具有強(qiáng)制性,在執(zhí)行完畢后進(jìn)程即被結(jié)束,因此位于其后的任何代碼將不能被執(zhí)行。雖然 ExitProcess()函數(shù)可以再結(jié)束進(jìn)程同時(shí)通知與其關(guān)聯(lián)的動(dòng)態(tài)鏈接庫(kù),但是由于他的這種強(qiáng)制性,使得ExitProcess()函數(shù)在使用上將存有安全隱患。例如,如果最親愛(ài)程序調(diào)用ExitProcess()函數(shù)之前曾用new操作,申請(qǐng)一段空間,那么敬愛(ài)那個(gè)會(huì)由于ExitProcess() 函數(shù)的強(qiáng)制性而無(wú)法通過(guò)delete操作符將其釋放,從而造成內(nèi)存泄露。
有鑒于ExitProcess()函數(shù)的強(qiáng)制性和安全性,在使用時(shí)一定要引起注意。
Terminateprocess()
ExitProcess 只能強(qiáng)制本進(jìn)程的推出,如果要在一個(gè)進(jìn)程中強(qiáng)制結(jié)束其他的進(jìn)程就需要用TerminateProcess()來(lái)實(shí)現(xiàn),與ExitProcess()不同,TerminateProcess()函數(shù)執(zhí)行后,被終止的進(jìn)程不會(huì)得到任何關(guān)于程序退出的通知。也就是說(shuō),被終止的進(jìn)程是無(wú)法再結(jié)束運(yùn)行前進(jìn)程推出前的收尾工作的。所以,通常只有在其他任何地方都無(wú)法迫使進(jìn)程退出時(shí)才會(huì)考慮使用TerminateProcess()去強(qiáng)制結(jié)束進(jìn)程。
BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode);
參數(shù)hProcess和uExitCode分別為進(jìn)城句柄和退出代碼。如果被結(jié)束的是本進(jìn)程,可以通過(guò)GetCurrentProcess()獲取到句柄。 TerminateProcess()是異步執(zhí)行的,在調(diào)用后返回并不能確定被終止進(jìn)程是否已經(jīng)真的退出,如果調(diào)用TerminateProcess() 的進(jìn)程對(duì)此細(xì)節(jié)關(guān)心,可以通過(guò)WaitForSingleObject()來(lái)等待進(jìn)程的真正結(jié)束。
在VC中如何結(jié)束系統(tǒng)正在運(yùn)行的其他進(jìn)程(該進(jìn)程必須有窗口界面),其實(shí)很簡(jiǎn)單,按照如下步驟進(jìn)程:
1)取得進(jìn)程的句柄(利用FindWindow函數(shù)得到);
2)獲取進(jìn)程ID號(hào)(用GetWindowThreadProcessId函數(shù)獲?。?;
3)打開進(jìn)程,OpenProcess函數(shù)中的第一個(gè)參數(shù)設(shè)為PROCESS_TERMINATE,就可以獲取處理該進(jìn)程的句柄;
4)利用TerminateProcess函數(shù)結(jié)束進(jìn)程,將該函數(shù)的第二個(gè)參數(shù)設(shè)為4.
代碼如下:
//結(jié)束進(jìn)程
int CStaticFunc::KillProcess(LPCSTR pszClassName, LPCSTR pszWindowTitle) { HANDLE hProcessHandle; ULONG nProcessID; HWND TheWindow; TheWindow = ::FindWindow( NULL, pszWindowTitle ); ::GetWindowThreadProcessId( TheWindow, &nProcessID ); hProcessHandle = ::OpenProcess( PROCESS_TERMINATE, FALSE, nProcessID ); return ::TerminateProcess( hProcessHandle, 4 ); }
而啟動(dòng)進(jìn)程則只需要CreateProcess函數(shù)就可完成,需要注意的是這個(gè)函數(shù)的幾個(gè)輸入?yún)?shù),第一個(gè)參數(shù)是
//啟動(dòng)新進(jìn)程 int CStaticFunc::CreateNewProcess(LPCSTR pszExeName) { PROCESS_INFORMATION piProcInfoGPS; STARTUPINFO siStartupInfo; SECURITY_ATTRIBUTES saProcess, saThread; ZeroMemory( &siStartupInfo, sizeof(siStartupInfo) ); siStartupInfo.cb = sizeof(siStartupInfo); saProcess.nLength = sizeof(saProcess); saProcess.lpSecurityDescriptor = NULL; saProcess.bInheritHandle = true; saThread.nLength = sizeof(saThread); saThread.lpSecurityDescriptor = NULL; saThread.bInheritHandle = true; return ::CreateProcess( NULL, (LPTSTR)pszExeName, &saProcess, &saThread, false,Create_DEFAULT_ERROR_MODE, NULL, NULL,&siStartupInfo,&piProcInfoGPS ); }
下面來(lái)看下如下很簡(jiǎn)單的示例程序:
// exitprocess_text.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。 // #include "stdafx.h" #include "windows.h" #include "iostream" class Example { public: Example() { std::cout<<"struction"<<std::endl; } ~Example() { std::cout<<"construction"<<std::endl; } }; Example ex1; int _tmain(int argc, _TCHAR* argv[]) { Example ex2; return 0; }
程序這樣執(zhí)行時(shí)最完美的,其輸出結(jié)果如下:
structionstructionconstructionconstruction
局部對(duì)象ex1和全局對(duì)象ex2都被正常析構(gòu),而如果將主函數(shù)該為如下:
int _tmain(int argc, _TCHAR* argv[]) { Example ex2; ::ExitProcess(0); return 0; }
程序此時(shí)執(zhí)行結(jié)果為:
structionstruction
局部對(duì)象和全局對(duì)象都沒(méi)被析構(gòu),因?yàn)檎{(diào)用了ExitProcess進(jìn)程直接結(jié)束,而沒(méi)有調(diào)用啟動(dòng)函數(shù)中的exit函數(shù),所以全局對(duì)象也沒(méi)被析構(gòu)。
而如果將主函數(shù)再改為如下:
int _tmain(int argc, _TCHAR* argv[]) { Example ex2; exit(0); return 0; }
程序此時(shí)執(zhí)行結(jié)果為:
structionstructionconstruction
可以看到只析構(gòu)了一個(gè)對(duì)象,而另外一個(gè)未被析構(gòu),其實(shí)沒(méi)被析構(gòu)的對(duì)象是局部對(duì)象,前面提到exit函數(shù)主要任務(wù)就是負(fù)責(zé)析構(gòu)全局對(duì)象和變
量,而不負(fù)責(zé)局部對(duì)象的析構(gòu)。為了說(shuō)明析構(gòu)的是全局變量,將主函數(shù)再做如下處理:
int _tmain(int argc, _TCHAR* argv[]) { exit(0); return 0; }
代碼的執(zhí)行結(jié)果是:
structionconstruction
由此證明析構(gòu)的是全局變量;如果你認(rèn)為還不給力的話,試著把全局對(duì)象刪掉,局部對(duì)象留下,其執(zhí)行結(jié)果是:
struction
局部變量會(huì)被證明沒(méi)被析構(gòu),這絕度沒(méi)有任何含糊。
如對(duì)本文有疑問(wèn),請(qǐng)?zhí)峤坏浇涣髡搲?,廣大熱心網(wǎng)友會(huì)為你解答!! 點(diǎn)擊進(jìn)入論壇