[cpp] view plaincopyprint?
1. int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE,PSTR pszCmdLine, int nCmdShow);
2.
3. int WINAPT wWinMain(HINSTANCE hinstExe,HINSTANCE,PWSTR pszCmdLine,int nCmdShow);
4.
5. int __cdecl main(int argc,char *argv[],char *envp[]);
6.
7. int _cdecl wmain(int argc, wchar_t *argv[],wchar_t *envp[]);
前兩個爲GUI的入口函數,後兩個爲CUI的入口函數;事實上,在一個進程開始運行時,WINDOWS OS並不直接從主函數開始執行,而是從另外
一個比較大的運行期啓動函數開始執行,不同的入口函數對應的啓動函數不同:
應用程序類型 進入點 嵌入可執行文件的啓動函數
需要ANSI字符和字符串的GUI應用程序 WinMain WinMainCRTStartup
需要Unicode字符和字符串的GUI應用程序 WinMain WinMainCRTStartup
需要ANSI字符和字符串的CUI應用程序 main mainCRTStartup
需要Unicode字符和字符串的CUI應用程序 wmain wmainCRTStartup
啓動函數負責對應用程序運行前期的初始化,如全局變量的內存分配等。如果編寫了一個wWinMain()函數,以下就是它的調用過程:
[cpp] view plaincopyprint?
1. GetStartupInfo(&StartupInfo);
2. int nMainRetVal = wWinMain(GetMjduleHandle(NULL),
3. NULL, pszCommandLineUnicode,
4. (StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ?
5. StartupInfo.wShowWindow:SW_SHOWDEFAULT);
6.
7. ......
8.
9. exit(0)
當入口函數(主函數)返回時,運行期啓動函數就執行EXIT函數,此函數主要完成全局對象和變量的內存釋放任務,之後:再調用ExitProcess
函數進行撤銷進程。即:exit()函數內部調用了ExitProcess函數。通常來說,這是最完美的進程執行過程。由此可以看出eixt()函數原型:
ExitProcess()函數實際上只是用來進行結束進程,如果其後面還有我們預期要執行的代碼,實際上全未執行,這個性質暴露出ExitProcess函數
的缺陷:可能導致已經創建的對象沒有析構而退出,從而導致內存泄露。
TerminateProcess()函數的實際作用跟ExitProcess函數差不多,只不過,此函數可用來終止當前進程之外的另外一個其它進程,同樣的,它
也會導致和ExitProcess一樣的結果:內存泄露。
如果我們在編寫應用程序時,打算終止當前進程,我們該調用哪個函數?答案是:三者其實都一樣! 因爲三者都可能導致內存泄露,但我們擔心
的過多了,因爲進程在結束時,即使有ExitProcess,TerminateProcess,以及exit函數調用而導致的內存泄露,OS也會進行清理工作,能保證
我們泄露的內存最終被還回到OS中去,而不必擔心當前進程已經退出而導致內存泄露,致使其它進程無法使用該內存塊。一個進程無論在什麼情
況下終止,都會進行如下工作:
1) 進程指定的所有用戶對象和G D I對象均被釋放,所有內核對象均被關閉(如果沒有其他 進程打開它們的句柄,那麼這些內核對象將被撤消。
但是,如果其他進程打開了它們的句柄, 內核對象將不會撤消)。
2) 進程的退出代碼將從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) 進程內核對象的狀態變成收到通知的狀態(關於傳送通知的詳細說明,參見第9章)。系 統中的其他線程可以掛起,直到進程終止運行。
4) 進程內核對象的使用計數遞減1。
下面來看下如下很簡單的示例程序:
[cpp] view plaincopyprint?
1. // exitprocess_text.cpp : 定義控制檯應用程序的入口點。
2. //
3.
4. #include "stdafx.h"
5. #include "windows.h"
6. #include "iostream"
7.
8. class Example
9. {
10. public:
11.
12. Example()
13. {
14. std::cout<<"struction"<<std::endl;
15.
16. }
17.
18. ~Example()
19. {
20. std::cout<<"construction"<<std::endl;
21. }
22. };
23.
24. Example ex1;
25.
26. int _tmain(int argc, _TCHAR* argv[])
27. {
28.
29. Example ex2;
30.
31. return 0;
32. }
程序這樣執行時最完美的,其輸出結果如下:
structionstructionconstructionconstruction
局部對象ex1和全局對象ex2都被正常析構,而如果將主函數該爲如下:
[cpp] view plaincopyprint?
1. int _tmain(int argc, _TCHAR* argv[])
2. {
3.
4. Example ex2;
5.
6. ::ExitProcess(0);
7.
8. return 0;
9. }
程序此時執行結果爲:
structionstruction
局部對象和全局對象都沒被析構,因爲調用了ExitProcess進程直接結束,而沒有調用啓動函數中的exit函數,所以全局對象也沒被析構。
而如果將主函數再改爲如下:
[cpp] view plaincopyprint?
1. int _tmain(int argc, _TCHAR* argv[])
2. {
3.
4. Example ex2;
5.
6. exit(0);
7.
8. return 0;
9. }
程序此時執行結果爲:
structionstructionconstruction
可以看到只析構了一個對象,而另外一個未被析構,其實沒被析構的對象是局部對象,前面提到exit函數主要任務就是負責析構全局對象和變
量,而不負責局部對象的析構。爲了說明析構的是全局變量,將主函數再做如下處理:
[cpp] view plaincopyprint?
1. int _tmain(int argc, _TCHAR* argv[])
2. {
3.
4. exit(0);
5.
6. return 0;
7. }
代碼的執行結果是:
structionconstruction
由此證明析構的是全局變量;如果你認爲還不給力的話,試着把全局對象刪掉,局部對象留下,其執行結果是:
struction
局部變量會被證明沒被析構,這絕度沒有任何含糊。