VC++编程:MFC程序中的WinMain入口函数

2023-11-22 22:58

本文主要是介绍VC++编程:MFC程序中的WinMain入口函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


     程序之家转载文章:
     读者还记得我们在第2章中讲述的创建Win32应用程序的几个步骤吗?当时,我们介绍Win32应用程序有一条很明确的主线:首先进入WinMain函数,然后设计窗口类、注册窗口类、产生窗口、注册窗口、显示窗口、更新窗口,最后进入消息循环,将消息
路由到窗口过程函数中去处理。遵循这条主线,我们在写程序时就有了一条很清晰的脉络。


  但在编写MFC程序时,我们找不到这样一条主线,甚至在程序中找不到WinMain函数。可以在当前Test工程中查找WinMain函数,方法是在VC++开发环境中单击【Edit】菜单,选择【Find in Files…】菜单项,并在弹出的查找对话框中“Find What:”文本框内输入“WinMain”,单击【Find】按钮,结果当然是找不到WinMain函数。读者可以在这个工程中,再查找一下WNDCLASS、CreateWindow等,你会发现仍然找不到。那么是不是MFC程序就不需要WinMain函数,不需要设计窗口类,不需要创建窗口了呢?当然不是。我们之所以看不见这些,是因为微软在MFC的底层框架类中封装了这些每一个窗口应用程序都需要的步骤,目的主要是为了简化程序员的开发工作,但这也给我们在学习和掌握MFC程序时造成了很多不必要的困扰。


  为了更好地学习和掌握基于MFC的程序,有必要对MFC的运行机制,以及封装原理有所了解。在第1章就讲述了WinMain函数是所有Win32程序的入口函数,就像DOS下的main函数一样。我们创建的这个MFC程序也不例外,它也有一个WinMain函数,但这个WinMain函数是在程序编译链接时,由链接器将该函数链接到Test程序中的。


  在安装完Microsoft Visual Studio 6.0后,在安装目录下(将Microsoft Visual Studio 6.0安装到了D:\Program Files下),微软提供了部分MFC的源代码,我们可以跟踪这些源代码,来找出程序运行的脉络。机器上MFC源代码的具体路径为D:\Program Files\Microsoft Visual Studio\VC98\MFC\ SRC,读者可以根据这个目录结构在自己机器上查找相应的目录。找到相应的目录后,在资源浏览器的工具栏上选择“搜索”。然后在搜索窗口的“包含文字”文本框中输入”WinMain”,单击“立即搜索(S)”按钮,搜索结果如图3.12所示。




图片



图3.12 包含“WinMain”文字的搜索结果


我们只需要查看后缀名为CPP的源文件即可,实际上,WinMain函数在APPMODUL.CPP这个文件中。保持Test工程的打开状态,然后双击APPMODUL.CPP即可在VC++环境中打开该文件,在其中可以找到如例3-1所示的这段代码。


  例3-1
  extern "C" int WINAPI
  _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  LPTSTR lpCmdLine, int nCmdShow)
  {
  // call shared/exported WinMain
  return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
  }


  WinMain函数找到了。现在我们可以看看Test程序是否会进入这个WinMain函数。在WinMain函数中按下F9键设置一个断点,然后按下F5键调试运行当前程序。我们发现程序确实运行到该断点处停了下来,如图3.13所示。这说明Test这个MFC程序确实有WinMain函数,在程序编译链接时,WinMain函数就成为该程序的一部分。



图片
图3.13 程序运行到WinMain断点处


  但这个_tWinMain函数和第1章所讲的WinMain函数有些不同,让我们先看看这个函数的定义。读者可以在_tWinMain上单击鼠标右键,从弹出的快捷菜单中选择【Go To Definition Of _tWinMain】菜单项,光标就会定位到_tWinMain函数的定义处,代码如例3-2所示,从中我们可以发现_tWinMain实际上是一个宏,展开之后就是WinMain函数。




例3-2
  #define _tmain main
  #define _tWinMain WinMain
  #ifdef _POSIX_
  #define _tenviron environ
  #else
  #define _tenviron _environ
  #endif
  #define __targv __argv


  1.theApp全局对象


  找到了WinMain函数,那么它是如何与MFC程序中的各个类组织在一起的呢?也就是说,MFC程序中的类是如何与WinMain函数关联起来的呢?


  双击ClassView标签页中的CTestApp类,跳转到该类的定义文件(Test.h)中。可以发现CTestApp派生于CWinApp类,后者表示应用程序类。我们在ClassView标签页中打开CTestApp类前面的“+”符号,双击该类的构造函数,就跳转到该类的源文件(Test.cpp)中。在CTestApp构造函数处设置一个断点,然后调试运行Test程序,将发现程序首先停在CTestApp类的构造函数处,继续运行该程序。这时程序才进入WinMain函数,即停在先前我们在WinMain函数中设置的断点处。


  在我们通常的理解当中,WinMain函数是程序的入口函数。也就是说,程序运行时首先应该调用的是WinMain函数,那么这里为什么程序会首先调用CTestApp类的构造函数呢?看一下CTestApp的源文件,可以发现程序中定义了一个CTestApp类型的全局对象:theApp。代码如下。


  // The one and only CTestApp object
  CTestApp theApp;


  提示:MFC程序的全局变量都放置在ClassView标签页的Globals分支下,展开该分支即可看到程序当前所有的全局变量。双击某个全局变量,即可定位到该变量的定义处。


  我们在这个全局对象定义处设置一个断点,然后调试运行Test程序,将发现程序执行的顺序依次是:theApp全局对象定义处、TestApp构造函数,然后才是WinMain函数。


  为了更好地解释这一过程,我们再新创建一个Win32控制台工程。单击【File】菜单,选择【New】菜单项, 在Projects选项卡下,选择Win32 Console Application类型,在右侧的Project name文本框中输入工程名:main,并将程序放置到适当的位置(即设置Location的内容),如图3.14所示。


图片
图3.14 新建Win32控制台应用

  单击【OK】按钮,进入“Win32 Console Application”向导,选择一个空工程即可,如图3.15所示。单击【Finish】按钮,向导就自动生成一个空的Win32控制台应用框架。


  接着为这个main工程新建一个源文件,方法是单击【File】菜单,选择【New】命令,在弹出的【New】对话框中选择【Files】选项卡,然后选择C++ Source File项,并在右侧的【File】文本框中输入源文件名:main,如图3.16所示。



图片
图3.15 Win32 Console Application向导



图片
图3.16 新建一个源文件



接下来,在main.cpp文件中输入如例3-3所示的代码。

  例3-3
  #include
  int a=6;
  void main()
  {
  cout<
  }


  上述代码非常简单,首先定义了一个int类型的全局变量a,并给它赋了一个初值6。然后定义了一个main函数,该函数所做的工作就是将全局变量a的值输出到标准输出cout上。因为使用了标准输出,所以需要包含相应的头文件:iostream.h,这是C++中的标准输入输出流头文件。


  我们在main函数处设置一个断点,调试运行该程序,将会发现程序在进入main函数时,a的值已经是6了。也就是说,在程序入口main函数加载时,系统就已经为全局变量或全局对象分配了存储空间,并为它们赋了初始值。


  小技巧:在程序运行过程中,如果想要查看某个变量的当前值,方法一是把鼠标移到该变量上,停留片刻,VC++就会弹出一个小窗口,此窗口中显示了该变量的当前值,如图3.17所示。



图片
图3.17 显示当前变量取值的小窗口


  方法二是利用VC++提供的调试窗口来查看变量的当前值。操作步骤是单击View菜单,选择Debug Windows选项,在下拉菜单中选择Variables菜单项,即可显示变量窗口,如图3.18所示。该窗口显示了程序当前上下文中的一些重要变量的当前值。



图片
图3.18 Variables窗口


这篇关于VC++编程:MFC程序中的WinMain入口函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/413512

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

java程序远程debug原理与配置全过程

《java程序远程debug原理与配置全过程》文章介绍了Java远程调试的JPDA体系,包含JVMTI监控JVM、JDWP传输调试命令、JDI提供调试接口,通过-Xdebug、-Xrunjdwp参数配... 目录背景组成模块间联系IBM对三个模块的详细介绍编程使用总结背景日常工作中,每个程序员都会遇到bu

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

python中的高阶函数示例详解

《python中的高阶函数示例详解》在Python中,高阶函数是指接受函数作为参数或返回函数作为结果的函数,下面:本文主要介绍python中高阶函数的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录1.定义2.map函数3.filter函数4.reduce函数5.sorted函数6.自定义高阶函数

Python中的sort方法、sorted函数与lambda表达式及用法详解

《Python中的sort方法、sorted函数与lambda表达式及用法详解》文章对比了Python中list.sort()与sorted()函数的区别,指出sort()原地排序返回None,sor... 目录1. sort()方法1.1 sort()方法1.2 基本语法和参数A. reverse参数B.

uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)

《uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)》在uni-app开发中,文件上传和图片处理是很常见的需求,但也经常会遇到各种问题,下面:本文主要介绍uni-app小程序项目中实... 目录方式一:使用<canvas>实现图片压缩(推荐,兼容性好)示例代码(小程序平台):方式二:使用uni

C++读写word文档(.docx)DuckX库的使用详解

《C++读写word文档(.docx)DuckX库的使用详解》DuckX是C++库,用于创建/编辑.docx文件,支持读取文档、添加段落/片段、编辑表格,解决中文乱码需更改编码方案,进阶功能含文本替换... 目录一、基本用法1. 读取文档3. 添加段落4. 添加片段3. 编辑表格二、进阶用法1. 文本替换2