WinMain函式
WinMain函式原型
Win32應用程式的入口函式為WinMain,函式原型在WinBase.h檔案中:
int WINAPI WinMain ( _In_ HINSTANCE hInstance, |
WinMain函式原型中的符號
符號 | 描述 | 其它 |
int | 返回值 | |
WINAPI | 函式呼叫約定 |
WINAPI巨集展開: #define WINAPI __stdcall |
WinMain | 函式名 | |
HINSTANCE hInstance | 當前應用程式例項控制程式碼 |
HINSTANCE巨集展開: DECLARE_HANDLE(HINSTANCE); #define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name |
HINSTANCE hPrevInstance | 沒有意義。 它用於 16 位Windows,但現在始終為零 | |
LPSTR lpCmdLine | 命令列引數 | |
int nShowCmd | 視窗顯示控制引數 |
程式入口
Win32程式設計入口為什麼是WinMain呢?
C/C++語言中,程式設計入口為什麼是main,而不是其它的呢?
CPU控制權傳遞
CPU是計算機的大腦,CPU讀取指令後執行指令,即:控制CPU讀指令的位置,就控制了CPU。
計算機加電後,CPU從BIOS指定位置讀取並執行開機程式第一條指令,即,CPU被開機程式控制,開機程式獲得了CPU的控制權。
開機程式獲得CPU控制權後,其它程式如何獲得CPU控制權呢?開機程式約定,我會去讀取哪個地址的指令來執行。
其它程式把指令寫入開機程式約定的地方,CPU的控制權就到了其它程式。
CPU控制權一步一步傳遞到作業系統。
CPU控制權到了作業系統後,其它程式想要獲得控制權,就需要遵循作業系統的約定。
作業系統約定包括PE格式等,首先按PE格式生成可執行檔案,如.exe。
雙擊或者呼叫程式建立函式啟動程式,作業系統將可執行程式裝載到記憶體,將下一條讀取地址指向程式的入口地址,這樣CPU的控制權傳遞到了可執行程式。
PE檔案生成
要執行自定義的指令,就要遵循與作業系統的約定。
作業系統要求裝載PE格式的檔案,可以通過編譯工具編譯生成PE格式檔案,如.exe。
程式語言
編譯工具負責生成PE格式檔案。
編譯工具約定了輸入的格式,即程式語言。
程式入口
C/C++程式語言的編譯工具約定,可執行程式的入口預設是mainCRTStartup,該入口函式可以通過設定重新指定。
入口函式
C/C++程式語言約定了程式的入口函式,如main。程式入口(mainCRTStartup)呼叫入口函式main。
不論是main還是WinMain都只是一種約定,可以根據需要制定約定。
WinMain和main
C/C++語言的入口函式都是main,為什麼Win32是WinMain?
程式入口
程式入口可以通過編譯器指定,根據需要指定入口點,控制檯預設入口為mainCRTStartup,Win32預設入口為WinMainCRTStartup,也設定為自定義函式。
main呼叫
main呼叫可以通過exe_main.cpp,exe_common.inl檔案檢視
mainCRTStartup()->__scrt_common_main()->__scrt_common_main_seh()->invoke_main()->main()。
WinMain呼叫
WinMain呼叫可以通過exe_winmain.cpp,exe_common.inl檔案檢視
WinMainCRTStartup()->__scrt_common_main()->__scrt_common_main_seh()->invoke_main()->WinMain()。
為什麼是WinMain不是main?
不管是main還是WinMain都是程式語言的約定,約定內容包括返回值,呼叫約定,函式名,函式引數。
main和WinMain引數都包含命令列引數。
對Win32應用程式來說,主函式大概率會用到應用程式例項,即HINSTANCE hInstance引數。
為了給Win32應用程式開發提供便捷,制定了更適合Win32應用程式開發的介面約定WinMain。
應用程式例項也可以通過GetModuleHandle獲取,也就是說,只要通過設定,用main或者自定義的函式作為主函式也可以編寫Win32應用程式。
構建原理
入口函式缺失
在控制檯程式中,不寫main函式,編譯報錯:
1>MSVCRTD.lib(exe_main.obj) : error LNK2019: 無法解析的外部符號 main,函式 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中引用了該符號
在Win32程式中,不寫WinMain函式,編譯報錯:
1>MSVCRTD.lib(exe_winmain.obj) : error LNK2019: 無法解析的外部符號 WinMain,函式 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中引用了該符號
LIB檔案
上面的MSVCRTD.lib(exe_main.obj) 是什麼意思?
.lib檔案可以是靜態連結庫,也可以是匯入庫。
靜態連結庫包含可執行程式碼,匯入庫包含匯入庫資訊。可以通過lib命令檢視lib檔案的型別。
MSVCRTD.lib是一個靜態連結庫。
exe_main.obj是MSVCRTD.lib中的一部分。
連結
連結時,由於程式入口為mainCRTStartup或WinMainCRTStartup,因此連結時會對程式入口的依賴進行檢查。
main連結時,exe_main.obj包含mainCRTStartup()。
mainCRTStartup()->__scrt_common_main()->__scrt_common_main_seh()->invoke_main()->main()。
如果不寫main函式,連結找不到main函式,連結報錯。
main連結時,exe_winmain.obj包含WinMainCRTStartup()。
WinMainCRTStartup()->__scrt_common_main()->__scrt_common_main_seh()->invoke_main()->WinMain()。
如果不寫WinMain函式,連結找不到WinMain函式,連結報錯。