`
shfzhzhr
  • 浏览: 69443 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

windows应用程序的生命周期

阅读更多

 

Windows支持两种类型的应用程序:GUICUI程序。前者是Graphical User Interface的简称,后者是Console User Interface的简称。

 

VS创建一个项目的时候,IDE会设置各种链接器开关,使链接器将子系统(注1)的正确类型嵌入最终生成的可执行文件中。对于CUI程序,这个链接器开关是/SUBSYSTEM:CONSOLE,对于GUI程序,则是/SUBSYSTEM:WINDOWS。用户运行应用程序时,操作系统的加载程序会检查可执行文件映像的文件头,并获取这个子系统值。如果此值表明是一个CUI程序,加载程序会确保有一个可用的文本控制台窗口。如果是GUI程序,加载器就不会创建控制台窗口;相反,它只是加载这个程序,一定应用程序开始运行,操作系统就不再关心程序的界面是什么了。

 

Windows应用程序必须有一个入口函数,应用程序开始运行时,这个函数会被调用。C/C++开发人员可以使用以下两种入口点函数。

int WINAPI _tWinMain(

    HINSTANCE hInstanceExe,

    HINSTANCE,

    PTSTR pszCmdLine,

    int nCmdShow);

 

int _tmain(

    int argc,

    TCHAR *argv[],

    TCHAR *envp[]);

 

注意,具体的符号取决于我们是否要使用Unicode字符串。操作系统实际并不调用我们所写的入口点函数,相反,它会调用由C/C++运行库实现并在连接时使用-entry:命令行选项来设置一个C/C++运行时启动函数。该函数将初始化C/C++运行库,使我们能调用mallocfree之类的函数。它还确保了在我们的代码开始执行之前,我们声明的任何全局和静态C++对象都被正确地构造。下表总结了源代码要实现什么入口点函数,以及每个入口点函数应该在什么时候使用。

应用程序类型

入口点函数

嵌入可执行文件的启动函数

处理ANSI字符和字符串的GUI应用程序

_tWinMain(WinMain)

WinMainCRTStartup

处理unicode字符和字符串的GUI程序

_tWinMain(wWinMain)

wWinMainCREStartup

处理ANSI字符和字符串的CUI程序

_tmain(Main)

mainCRTStartup

处理unicode字符和字符串的CUI程序

_tmain(Wmain)

wmainCRTStartup

 

在链接可执行文件时,链接器将选择正确的C/C++运行库启动函数,如果指定了/SUBSYSTEM:WINDOWS链接器开关,链接器就会寻找WinMainwWinMain函数,如果这两个函数都没有找到,链接就会返回一个“unresolved external sumbol”错误,否则它将根据具体情况分别选择WinMainCRTStartup或者wWinMainCRTStartup

类似的,如果指定了/SUBSYSTEM:CONSOLE链接器也遵循同样的规则。

 

一个鲜为人知的事实是,我们完全可以从自己的项目中移除/SUBSYSTEM链接器开关,一旦这样做,链接器就会自动判断应该为应用程序设置为哪个子系统。

 

所有C/C++运行库启动函数所做的事情基本都是一样的,区别在于他们要处理的是ANSI还是Unicode字符串;以及在初始化C运行库之后,他们调用的是哪一个入口函数。Visual C++自带了C运行库源码。可以在crtexe.c文件中找到4个启动函数的源码,这些启动函数的用途简单总结如下:

l  获取指向新进程的完整命令行的一个指针。

l  获取执行新进程的环境变量的一个指针。

l  初始化C/C++运行库的全局变量。如果包含了stdlib.h,我们的代码可以访问这些变量。下表总结了这些变量。

l  初始化C运行库的内存分配函数(malloccalloc)和其他底层I/O例程使用的堆。

l  调用所有全局和静态C++类对象的构造函数。

变量名称

类型

描述和推荐使用的windows函数

_osver

unsigned int

操作系统的构建版本号。例如:windows vista RTMbuild 6000,所以_osver的值就是6000。请换用GetVersionEx

_winmajor

unsigned int

16进制表示的windows系统的主版本号,对于vista6。请换用GetVersionEx

_winminor

unsigned int

16进制表示的windows系统的次版本号,对于vista0。请换用GetVersionEx

_winver

unsigned int

(_winmajor<<8)+_winminor。请换用GetVersionEx

_argc

unsigned int

命令行上传递的参数个数。请换用GetCommandLine

_argv

_wargv

char

wchar_t

长度为_argc的一个数组,每一项指向一个命令行参数。请换用GetCommandLine

_environ

_wenviron

char

wchar_t

一个指针数组,数组中的每一项指向一个环境字符串。请换用GetEnvironmentStringsGetEnvironmentVariable

_pgmptr

_wpgmptr

char

wchar_t

正在运行的程序的名称及其ANSI/Unicode完整路径。请换用GetModuleFileName,将NULL作为第一个参数传进去。

 

    完成这些初始化工作以后,C/C++才会调用我们的入口函数。

 

    入口函数返回后,启动函数将调用C运行库的exit函数,向其传递入口函数的返回值。Exit函数将执行以下操作:

l  调用_onexit函数调用所注册的任何一个函数。

l  调用所有全局和静态C++类对象的析构函数。

l  DEBUG生成中,如果设置了_CRTDBG_LEAK_CHECK_DF标志,就通过调用_CrtDumpMemoryLeaks函数生成内存泄漏报告。

l  调用操作系统的ExitProcess函数,向其传递入口函数的返回值。这会导致操作系统杀死我们的进程,并设置它的退出代码。

 

注意,为了安全起见,Microsoft不赞成使用所有这些变量,因为使用了这些变量的代码可能会在C运行库初始化这些变量之前运行。



注1: Windows最初曾有三个环境子系统:POSIXOS/2Windows,但是伴随着Windows2000的巨大成功,WIndows不再将posix2000以后的系统一起发行了。(具体可参见潘爱民的深入理解windows

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics