黑基网 首页 服务器 Windows Server 查看内容

程序的启动和退出过程详解

2015-3-6 17:38| 投稿: winserver

摘要: Windows支持两种类型的应用程序。一种是基于图形用户界面(GUI)的应用程序,另一种是基于控制台用户界面(CUI)的应用程序。 基于G U I 的应用程序有一个图形前端程序。它能创建窗口,拥有...
Windows支持两种类型的应用程序。一种是基于图形用户界面(GUI)的应用程序,另一种是基于控制台用户界面(CUI)的应用程序。 基于G U I 的应用程序有一个图形前端程序。它能创建窗口,拥有菜单,可以通过对话框与用户打交道,并可使用所有的标准“Wi n d o w s ”组 件。Wi n d o w s 配备的所有应用程序附件(如Notepad 、Calculator 和WordPad ),几乎都是基于G U I 的应用程序。基 于控制台的应用程序属于文本操作的应用程序。它们通常不能用于创建窗口或处理消息,并且它们不需要图形用户界面。虽然基于C U I 的应用程序 包含在屏幕上的窗口中,但是窗口只包含文本。命令外壳程序CMD.EXE(用于Windows 2000 )和COMMAND.COM (用于Windows 98)都是典型的 基于CUI的应用程序。     这两种类型的应用程序之间的界限是非常模糊的。可以创建用于显示对话框的C U I 应用程序。例如,命令外壳程序可能拥有一个特殊的命令,使它 能够显示一个图形对话框,在这个对话框中,可以选定你要执行的命令,而不必记住该外壳程序支持的各个不同的命令。也可以创建一个基于G U I 的应用程序,它能将文本字符串输出到一个控制台窗口。我常常创建用于建立控制台窗口的G U I 应用程序,在这个窗口中,我可以查看应用程序执 行时的调试信息。当然你也可以在应用程序中使用图形用户界面,而不是老式的字符界面,因为字符界面使用起来不太方便。     当使用Microsoft Visual C++来创建应用程序时,这种集成式环境安装了许多不同的链接程序开关,这样,链接程序就可以将相应的子系统嵌入产生的可执行程序。用于C U I 应用程序的链接程序开关是/ S U B S Y S T E M : C O N D O L E ,而用于G U I 应用程序的链接程序开关是SUBSYSTEM : WINDOWS 。当用户运行一个应用程序时,操作系统的加载程序就会查看可执行图形程序的标题,并抓取该子系统的值。如果该值指明一个CUI应用程序,那么加载程序就会 自动保证为该应用程序创建文本控制台窗口。 如果该值指明这是个G U I 应用程序,那么加载程序不创建控制台窗口,而只是加载应用程序。一旦应用程序启动运行,操作系统就不再考虑应用程 序拥有什么类型的用户界面。Wi n d o w s 应用程序必须拥有一个在应用程序启动运行时调用的进入点函数。可以使用的进入点函数有4 个:int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE, PSTR pszCmdLine, int nCmdShow);    int WINAPT wWinMain(HINSTANCE hinstExe,HINSTANCE, PWSTR pszCmdLine,int nCmdShow);    int __cdecl main(int argc,char *argv[],char *envp[]);    int _cdecl wmain(int argc, wchar_t *argv[], wchar_t *envp[]);       操作系统实际上并不调用你编写的进入点函数。它调用的是C / C + +运行期启动函数。该函数负责对C / C + +运行期库进行初始化,这样,就可以 调用m a l l o c 和f r e e 之类的函数。它还能够确保已经声明的任何全局对象和静态C + +对象能够在代码执行以前正确地创建。下面说明源代 码中可以实现哪个进入点以及何时使用该进入点(见表4 - 1 )。 表4-1 应用程序的进入点 应用程序类型 进入点 嵌入可执行文件的启动函数 需要ANSI字符和字符串的GUI应用程序 WinMain WinMainCRTStartup 需要Unicode字符和字符串的GUI应用程序 wWinMainw WinMainCRTStartup 需要ANSI字符和字符串的CUI应用程序 main mainCRTStartup 需要Unicode字符和字符串的CUI应用程序 wmain wmainCRTStartup     链接程序负责在它连接可执行文件时选择相应的C / C + +运行期启动函数。如果设定了/ S U B S Y S T E M : W I N D O W S 链接程序开关,那 么该链接程序期望找到一个Wi n M a i n 或w Wi n m a i n函数。如果这两个函数都不存在,链接程序便返回一个“未转换的外部符号”的错误消 息。否则,它可以分别选择Wi n M a i n C RT S t a r t u p 函数或w Wi n M a i n C RT S t a r t u p 函数。 同样,如果设定了/ S U B S Y S T E M : C O N S O L E 链接程序开关,那么该链接程序便期望找到m a i n 或w m a i n 函数,并且可以分别选 择m a i n C RT S t a r t u p 函数或w m a i n C RT S t a r t u p 函数。同样,如果m a i n 或w m a i n 都不存在,那么链接程序返回一条 “未转换外部符号”的消息。     但是,人们很少知道这样一个情况,即可以从应用程序中全部删除/ S U B S Y S T E M 链接程序开关。当这样做的时候,链接程序能够自动确定应 用程序应该连接到哪个子系统。当进行链接时,链接程序要查看代码中存在4 个函数(Wi n M a i n 、w Wi n M a i n 、m a i n 或w m a i n ) 中的哪一个。然后确定可执行程序应该是哪一个子系统,并且确定可执行程序中应该嵌入哪个C / C + +启动函数。     Wi n d o w s / Visual C++编程新手常犯的错误之一是,当创建新的应用程序时,不小心选择了错误的应用程序类型。例如,编程员可能创建一个 新的Wi n 3 2 应用程序项目,但是创建了一个进入点函数m a i n 。当创建应用程序时,编程员会看到一个链接程序错误消息,因为w i n 3 2 应 用程序项目设置了/ S U B S Y S T E M : W I N D O W S 链接程序开关,但是不存在Wi n M a i n 或w Wi n M a i n 函数。这时,编程员可以有 4 个选择: 将m a i n 函数改为Wi n M a i n 。通常这不是最佳的选择,因为编程员可能想要创建一个控制台应用程序。用Visual C++创建一个新的Win32 控制台应用程序,并将现有的源代码添加给新应用程第4 章进程计计47 下载序项目。这个选项冗长而乏味,因为它好像是从头开始创建应用程序,而且必须删除原始的应用程序文件。单 击Project Settings 对话框的L i n k 选项卡,将/ S U B S Y S T E M : W I N D O W S 开关改为/ S U B S Y S T E M : C O N S O L E 。这是解决问题的一种比较容易的方法,很少有人知道他们只需要进行这项操作就行了。单击Project Settings 对话框的L i n k 选项卡,然后全部删除/ S U B S Y S T E M : W I N D O W S 开关。这是我喜欢选择的方法,因为它提供了最大的灵活性。现在,连接程序将根据源代码中实现的函数进行正确的操作。当用Visual C++的Developer Studio 创建新Wi n 3 2 应用程序或Wi n 3 2 控制台应用程序项目时,我不知道为什么这没有成为默认设置。     所有的C / C + +运行期启动函数的作用基本上都是相同的。它们的差别在于,它们究竟是处理A N S I 字符串还是U n i c o d e 字符串,以及它 们在对C 运行期库进行初始化后它们调用哪个进入点函数。Visual C++配有C 运行期库的源代码。可以在CR t0.c 文件中找到这4 个启动函数的代码 。 现在将启动函数的功能归纳如下:  检索指向新进程的完整命令行的指针。检索指向新进程的环境变量的指针。 对C / C + +运行期的全局变量进行初始化。如果包含了S t d L i b . h 文件,代码就能访问这些变量。表4 - 1 列出了这些变量。 对C 运行期内存单元分配函数(m a l l o c 和c a l l o c )和其他低层输入/输出例程使用的内存栈进行初始化。•为所有全局和静态C + +类对象调用构造函数。     当所有这些初始化操作完成后,C / C + +启动函数就调用应用程序的进入点函数。如果编写了一个w Wi n M a i n 函数,它将以下面的形式被调用 :GetStartupInfo(&StartupInfo);  int nMainRetVal = wWinMain(GetMjduleHandle(NULL),     NULL, pszCommandLineUnicode,     (StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ?      StartupInfo.wShowWindow:SW_SHOWDEFAULT);       如果编写了一个Wi n M a i n 函数,它将以下面的形式被调用:GetStartupInfo(&StartupInfo);     int nMainReLVal = WinMain(GetModuleHandle(NULL),     NULL, pszCommandLineANSI,     (StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ?      Startupinfo.wShowWindow:SW_SHOWDEFAULT);       如果编写了一个w m a i n或m a i n 函数,它将以下面的形式被调用:int nMainRetVal = wmain(__argc, __wargv, _wenviron);     int nMainRetVal = main(_argc, __argv, _environ);   当进入点函数返回时,启动函数便调用C 运行期的e x i t 函数,将返回值(n M a i n R e t Va l )传递给它。E x i t 函数负责下面的操作 调用由_onexit函数的调用而注册的任何函数。为所有全局的和静态的C++类对象调用析构函数。调用操作系统的ExitProcess函数,将nMainRetVal传递给它。这使得该操作系统能够撤消进程并设置它的e x i t 代码。 表4 - 2 显示了程序能够使用的C / C + +运行期全局变量。 表4-2 程序能够使用的C / C + +运行期全局变量 变量名 类型 说明 _osver unsigned int 操作系统的测试版本。例如Windows 2000 Beta 3是测试版本2031 。因此_osver的值是2031 _winmajor unsigned int 采用十六进制表示法的Windows主要版本。对于Windows2000来说,它的值是5 _winminor unsigned int 采用十六进制表示法的Windows次要版本。对于Windows2000来说,它的值是0 _winver unsigned int ( _winmajor < < 8 ) + _ winminor在命令行上传递的参数号 __argc unsigned int 带有指向ANSI/Unicode字符串的指针的__argc大小的数组 __argv char * * 带有指向ANSI/Unicode字符串的指针的__argc大小的数组 __wargv wchar_t * * 每个数组项均指向一个命令行参数 _environ char * * 指向ANSI/Unicode字符串的指针的数组。每个数组项指向一个环境字符串 _wenviron wchar_t * * 指向ANSI/Unicode字符串的指针的数组。每个数组项指向一个环境字符串 _pgmptr char * 正在运行的程序的ANSI/Unicode全路径和名字 _wpgmptr wchar_t * 正在运行的程序的ANSI/Unicode全路径和名字
小编推荐:欲学习电脑技术、系统维护、网络管理、编程开发和安全攻防等高端IT技术,请 点击这里 注册黑基账号,公开课频道价值万元IT培训教程免费学,让您少走弯路、事半功倍,好工作升职加薪!



免责声明:本文由投稿者转载自互联网,版权归原作者所有,文中所述不代表本站观点,若有侵权或转载等不当之处请联系我们处理,让我们一起为维护良好的互联网秩序而努力!联系方式见网站首页右下角。


鲜花

握手

雷人

路过

鸡蛋

相关阅读

最新评论


新出炉

返回顶部