启动函数和入口函数

ʕ •ᴥ•ʔ ɔ:

1 程序的启动函数和入口函数

Windows支持两种类型的应用程序:

  • GUI:图形化应用程序(图形用户界面程序)
  • CUI:控制台应用程序(控制台用户界面程序)

mainWinmain函数被称为入口函数,入口函数被调用前,会先运行启动函数,然后由启动函数来调用入口函数。

启动函数和入口函数在多字节编码(ANSI)和Unicode编码环境下是不一样的,具体对应关系如下:

4.png

也可指定/移除链接器开关(如果移除链接器开关,程序将会自动判断子系统),VS中设置如下:

  • /SUBSYSTEM:WINDOWS
  • /SUBSYSTEM:CONSOLE

入口函数

1
2
3
4
5
6
7
8
9
10
 int WINAPI _tWinMain(
HINSTANCE hInstanceExe,
HINSTANCE,
PTSTR pszCmdline,
int nCmdShow);

int _tmain(
int argc,
TCHAR *argv[],
TCHAR *envp[]);

启动函数

  1. 启动函数由KERNEL32.dll中的KERNEL32函数调用,我们无法查看KERNEL32.dll的源码。
  2. VC++自带C运行库的源代码,可以在crtexe.c中找到这4个启动函数的源代码。

2 CUI程序

调用顺序:KERNEL32 --> mainCRTStartup --> main

2.1 启动函数

启动函数工作流程:

  1. GetVersion() :获取当前运行平台的版本号。控制台程序运行在Windows模拟的DOS下,因此这里获取的版本号为MS-DOS的版本信息。

  2. _heap_init() :用于初始化堆空间。在函数实现中使用HeapCreate申请堆空间,申请空间的大小由_heap_init传递的参数决定。_sbh_heap_init函数用于初始化堆结构信息。堆结构的说明将在第7章详细讲解。

  3. GetCommandLineA() :获取命令行参数信息的首地址。

  4. _crtGetEnvironmentStringsA:获取环境变量信息的首地址。

  5. _setargv() :参数分析。此函数根据GetCommandLineA()获取命令行参数信息的首地址并进行参数分析,将分离出的参数的个数保存在全局变量_argc中,将分析出的每个命令行参数的首地址存放在数组中,并将这个字符指针数组的首地址保存在全局变量_argv中。这样就得到了命令行参数的个数,以及命令行参数信息。

  6. _setenvp() :环境变量分析。此函数根据_crtGetEnvironmentStringsA函数获取环境变量信息的首地址并进行分析,将得到的每条环境变量字符串的首地址存放在字符指针数组中,并将这个数组的首地址存放在全局变量env中。

  7. _cinit() :用于全局数据和浮点寄存器的初始化。全局对象和IO流等的初始化都是通过这个函数实现的。利用函数_initterm进行数据链初始化,这个函数由两个参数组成,类型为_PVFV*,这是一个函数指针数组,其中保留了每个初始化函数的地址。初始化函数的类型为_PVFV,其定义原型如下:

    typedef void (_cdecl *_PVFV) (void);

2.2 入口函数

1

3 GUI程序

3.1 启动函数

3.2 入口函数