translated and rephrased from Windows Via C/C++
當程式運行時,作業系統的裝載器(loader)在可執行檔的頭部中尋找子系統(subsystem)的資訊。如果子系統是/SUBSYSTEM:CONSOLE,裝置器會負責有一個可用的命令列節目給應用程式。而如果子系統是/SUB-SYSTEM:WINDOWS,裝載器不做任何行為,只是簡簡單單的裝置這個應用程式。一旦程式開始運行,作業系統並不關心你在運行何種GUI程式。
作業系統並不會調用你自己寫的程式入口檔案,相反它會先調用C/C++ 執行階段程式庫(CRT,在windows中是msvcrt)的入口函數,而在連結的時候設定一個-entry標誌。C執行階段程式庫的入口函數初始化一下C/C++的運行條件,使應用程式可以使用諸如malloc或free之類的功能,同時它保證程式中的所有全域和靜態C++對象被正確建立。
連結器在連結程式的各個二進位檔案以生產可執行檔的時候負責選擇入口函數的地址。不同入口函數如下:
Application Type |
Entry Point |
Startup Function Embedded in Your Executable |
GUI application that wants ANSI characters and strings |
_tWinMain (WinMain) |
WinMainCRTStartup |
GUI application that wants Unicode characters and strings |
_tWinMain (wWinMain) |
wWinMainCRTStartup |
CUI application that wants ANSI characters and strings |
_tmain (Main) |
mainCRTStartup |
CUI application that wants Unicode characters and strings |
_tmain (Wmain) |
wmainCRTStartup |
入口函數的選擇會根據你對連結GUI還是CUI子系統的設定而定,如果程式中沒有提供相應函數(WinMain wWinMain main wmain),則會報"unresolved external symbol"的錯誤。
在程式中也可以不設定/SUBSYSTEM,這樣程式會自動尋找程式中是否WinMain wWinMain main wmain函數中的某一個,然後設定需要連結入程式的CRT入口函數。
在VS中對子系統的設定如下:
CRT的啟動程式的代碼在crtexe.c中,它們所完成功能如下:
-擷取命令列的完整參數
-擷取進程的環境變數
-初始化C/C++運行時變數,這些運行時變數可以在StdLib.h中定義。這些CRT的運行時變數如下:
Variable Name |
Type |
Description and Recommended Windows Function Replacement |
_osver |
unsigned int |
The build version of the operating system. For example, Windows Vista RTM was build 6000. Thus, _osver has a value of 6000. Use GetVersionEx instead. |
_winmajor |
unsigned int |
A major version of Windows in hexadecimal notation. For Windows Vista, the value is 6. Use GetVersionEx instead. |
_winminor |
unsigned int |
A minor version of Windows in hexadecimal notation. For Windows Vista, the value is 0. Use GetVersionEx instead. |
_winver |
unsigned int |
(_winmajor << 8) + _winminor. Use GetVersionEx instead. |
__argc |
unsigned int |
The number of arguments passed on the command line. Use GetCommandLine instead. |
__argv __wargv |
char wchar_t |
An array of size __argc with pointers to ANSI/Unicode strings. Each array entry points to a command-line argument. Notice that __argv is NULL if _UNICODE is defined and __wargv is NULL if it is not defined. Use GetCommandLine instead. |
_environ _wenviron |
char wchar_t |
An array of pointers to ANSI/Unicode strings. Each array entry points to an environment string. Notice that _wenviron is NULL if _UNICODE is not defined and _environ is NULL if _UNICODE is defined. Use GetEnvironmentStrings or GetEnvironmentVariable instead. |
_pgmptr _wpgmptr |
char wchar_t |
The ANSI/Unicode full path and name of the running program. Notice that _pgmptr is NULL if _UNICODE is defined and _wpgmptr is NULL if it is not defined. Use GetModuleFileName, passing NULL as the first parameter, instead. |
-初始化CRT記憶體配置函數(malloc free)所使用的堆和一些底層的I/O程式
-調用全域和靜態C++對象的建構函式
When your entry-point function returns, the startup function calls the C run-time exit function, passing it your return value (nMainRetVal). The exit function does the following:
It calls any functions registered by calls to the _onexit function.
It calls destructors for all global and static C++ class objects.
In DEBUG builds, leaks in the C/C++ run-time memory management are listed by a call to the _CrtDumpMemoryLeaks function if the _CRTDBG_LEAK_CHECK_DF flag has been set.
It calls the operating system's ExitProcess function, passing it nMainRetVal. This causes the operating system to kill your process and set its exit code.
Notice that all these variables have been deprecated for security's sake because the code that is using them might be running before the C run-time library has the chance to initialize them. This is why you should rather directly call the corresponding functions of the Windows API.