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

来源:互联网 发布:阿里云 华东 华南 华北 编辑:程序博客网 时间:2024/03/28 22:14

     程序之家转载文章:
     读者还记得我们在第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窗口


原创粉丝点击