Windows应用程序的启动过程
操作系统实际上并不调用你编写的进入点函数。它调用的是C/C++ run-time startup function。该函数负责对C/C++ run-time library进行初始化,这样,就可以调用malloc和free之类的函数。它还能够确保已经声明任何全局对象和静态对象能够在代码执行以前正确的创建。
所有的C/C++ run-time startup function的作用是相同的,其差别在于:是处理ANSI字符串还是Unicode字符串,以及他们在对C run-time library进行初始化后,它们调用那个进入点函数。VC++配有C run-time library的源代码。可以在CRt0.c文件中找到这4个启动函数的代码。
启动函数的功能可概括如下:
l 检索指向新进程的完整命令行的指针
l 检索指向新进程的环境变量的指针
l 对C/C++ run time's global variables进行初始化。如果包含了stdlib.h头文件,你的代码就能访问这些变量。详见下表:
l 对C运行期内存分配函数(mallco和calloc)以及low-level input/output routines使用的heap(堆)进行初始化
l 为所有的全局和静态C++类对象调用构造函数
当上述这些初始化操作完成后,C/C++ run-time startup function就调用应用程序的进入点函数。
如果编写了一个WinMain函数,它将以如下的形式被调用:
GetStartupInfo( &StartupInfo );
int nMainRetVal = WinMain( GetModuleHandle(NULL), NULL, pszCommandLineAnsi,
(StartupInfo.dwFlags & STARTF_USESHOWWINDOW)
? StartupInfo.wShowWindow : SW_SHOWDEFAULT );
当进入点函数返回时,启动函数便调用C运行期的exit寒暑,将返回值(nMainRetVal)传递给它。
Exit函数负责如下操作:
l 调用由_onexit函数的调用而注册的任何函数
l 为所有全局的和静态的C++类对象调用析构函数
l 调用操作系统的ExitProcess函数,将nMainRetVal传递给它。这使得操作系统能够撤销此进程并设置它的exit code(该代码保存在该进程对应的内核对象中)。
C++中的global object都配置在程序的data segment中,如果没有明确的初
始化,将以0为内存中的初值。这个过程是在编译期实现的。
如果global object配有constructor的话,那么这个constructor要到程序的
startup阶段才会执行。这个过程叫静态初始化。
C++对于在何时初始化global object没有明确规定,但是保证在main函数执行
之前完成初始化,在main结束之前完成清除。一般的编译器都是在main的开始处插
入所谓的startup阶段进行初始化,在结束时插入exit阶段来进行清除操作。