win32创建窗口及其消息循环

30 篇文章 0 订阅

转自:http://anyzixin.blog.163.com/blog/static/365289122007103031657355/

http://anyzixin.blog.163.com/blog/#m=0&t=1&c=fks_080075086083083064085095074064082083083075093087084

尽管windows应用程序千变万化,令人眼花缭乱,但,消息机制和窗口过程却始终它们的基础,掌握了这两项技术,也就相当于把握住了问题的关键。


  如果你以前是c程序员或是mfc的忠实用户,只要你学习过c语言的语法,自己亲手编过一些简短的c程序,理解以下的win32编程基础也不是一件困难的事。


  一个最简单的win32程序 


  在以前的c语言编程中,一个最简单的程序可以只有两行。




  而要实现同样功能的windows程序却最少也要写几十行,这并不是说明windows应用程序效率低下,难于掌握,只是说明程序在windows环境下有更丰富的内涵。windows程序的效率其实不低,在所有的windows应用程序中,都有一个程序初始化的过程,这得用上几十条语句,这段初始化的代码对于任何windows应用程序而言,都是大同小异的。下面以一个实现最简单功能的程序easywin为例,说明windows程序的基本框架。


  打开visual c++ 6.0。 


  选择file菜单的new,在出现的对话框中,选择projects栏目(新建工程),并点取其下的win32 application项,表示使用win32环境创建应用程序。先在locatin(路径)中填入“c:\”,然后在project name(项目名称)中填入“easywin”,其它按照缺省设置)。单击ok按钮。


  再次选择file菜单的new,在出现的对话框中,选择files栏目(新建文件),并点取其下的c++ source file项,表示新建一个c++源文件。在右边的file栏中输入“easywin”,最后确定让add to project检查框打上勾 )。单击ok按钮。 






  在easywin.cpp文件中输入以下源程序代码。  
 


  程序输入完毕,即可编译执行。在窗口中击鼠标键或按esc键时,会弹出一个对话框以表示你的操作。


  其实,这个程序可以看成是所有win32应用程序的框架,在以后所有的程序中,你会发现它们都是在这个程序的基础之上再添加代码。




  winmain()函数 
 
  winmain()函数是应用程序开始执行时的入口点,通常也是应用程序结束任务退出时的出口点。它与dos程序的main()函数起同样的作用,有一点不同的是,winmain()函数必须带有四个参数,它们是系统传递给它的。winmain()函数的原型如下:




  第一个参数hinstance,是标识该应用程序当前的实例的句柄。它是hinstance类型,hinstance是handle of instance的缩写,表示实例的句柄。hinstance是一个很关键的数据,它唯一的代表该应用程序,在后面初始化程序主窗口的过程中需要用到这个参数。


  这里有两个概念,一个是实例,一个是句柄。实例代表的是应用程序执行的整个过程和方法,一个应用程序如果没有被执行,只是存在于磁盘上,那么就说它是没有被实例化的;只要一执行,则说该程序的一个实例在运行。句柄,顾名思义,指的是一个对象的把柄。在windows中,有各种各样的句柄,它们都是32位的指针变量,用来指向该对象所占据的内存区。句柄的使用,可以极大的方便windows管理其内存中的各种对象。 


  第二个参数是hprevinstance,它是用来标识该应用程序的前一个实例句柄。对于基于win32的应用程序来说,这个参数总是null。这是因为在win95操作系统中,应用程序的每个实例都有各自独立的地址空间,即使同一个应用程序被执行了两次,在内存中也会为它们的每一个实例分配新的内存空间,所以一个应用程序被执行后,不会有前一个实例存在的可能。也就是说,hprevinstance这个参数是完全没有必要的,只是为了提供与16位windows的应用程序形式上的兼容性,才保留了这个参数。在以前的16位windows环境下(如windows3.2),hprevinstance用来标识与hinstance相关的应用程序的前一个句柄。


  第三个参数是lpcmdline,是指向应用程序命令行参数字符串的指针。如在win95的“开始”菜单中单击“运行”,输入“easywin hello”,则此参数指向的字符串为“hello”。


  最后一个参数是ncmdshow,是一个用来指定窗口显示方式的整数。这个整数值可以是sw_show、sw_hide、sw_showmaximized、sw_showminimized等,关于这些值的含义,将在下一节说明。




  注册窗口类 


  一个应用程序可以有许多窗口,但只有一个是主窗口,它是与该应用程序的实例句柄唯一关联的。上面的例程中,创建主窗口的函数是initwindow()。 


  通常要对填充一个窗口类结构wndclass,然后调用registerclass()对该窗口类进行注册。每个窗口都有一些基本的属性,如窗口边框、窗口标题栏文字、窗口大小和位置、鼠标、背景色、处理窗口消息函数的名称等等。注册的过程也就是将这些属性告诉系统,然后再调用createwindow()函数创建出窗口。这也就象你去裁缝店订做一件衣服,先要告诉店老板你的身材尺寸、布料颜色、以及你想要的款式,然后他才能为你做出一件让你满意的衣服。


  在vc的帮助中,可以看到wndclass结构是这样定义的:




  在win95和winnt的具有新界面特性的系统中,为了支持新的窗口界面特性,还有一种扩展的窗口类型wndclassex,它的定义如下:




  wndclass和wndclassex这两个结构基本上是一致的,只是wndclassex结构中多了cbsize和hiconsm这两个成员。wndclass结构的各成员中,其注释后打了星号的表示该项应特别注意。


  wndclass结构的第一个成员style表示窗口类的风格,它往往是由一些基本的风格通过位的“或”操作(操作符位“|”)组合而成。下表列出了一些常用的基本窗口风格:




  在easywin应用程序中,是按如下方式对wndclass结构进行填充和注册的:




  可以看到,wc.style被设为cs_vredraw | cs_hredraw,表示只要窗口的高度或宽度发生变化,都会重画整个窗口。


  第二个成员lpfnwndproc的值为(wndproc)winproc。表明该窗口类的消息处理函数是winproc()函数。这里,要指定窗口的消息处理函数的远指针,输入消息处理函数的函数名称即可,必要时应该进行强制类型转换,将其转换成wndproc型。


  接下来的cbclsextra和wc.cbwndextra在大多数情况下都会设为0。


  然后的hinstance成员,给它的值是由winmain()传来的应用程序的实例句柄,表明该窗口与该实例是相关联的。事实上,只要是注册窗口类,该成员的值始终是该程序的实例句柄,你应该象背书一样记住它。


  下面的hicon,是让你给这个窗口指定一个图标,调用 loadicon( hinstance, idi_application ),可以调用系统内部预先定义好的标志符为idc_application的图标作为该窗口的图标。


  同样,调用loadcursor( null, idc_arrow )为该窗口调用系统内部预先定义好的箭头型鼠标。


  hbrbackground成员用来定义窗口的背景画刷颜色,也就是该窗口的背景色。调用getstockobject(white_brush)可以获得系统内部预先定义好的白色画刷作为窗口的背景色。


  上面的loadicon()、loadcursor()、getstockobject()都是windows的api函数,它们的用法可以参看vc的帮助,这里就不多介绍了。


  lpszmenuname成员的值我们给它null,表示该窗口将没有菜单。如果你想让你的窗口拥有菜单,就把lpszmenuname成员赋值为标志菜单资源的字符串。


  wndclass结构的最后一个成员lpszclassname是让你给这个窗口类起一个唯一的名称,因为windows操作系统中有许许多多的窗口类,必须用一个独一无二的名称来代表它们。通常,你可以用你的程序名来命名这个窗口类的名称。这个名称将在创建窗口的createwindow()函数中用到。


  填充完毕后,对于wndclass结构,调用registerclass()函数进行注册;对于wndclassex结构,调用registerclassex()函数进行注册,它们的原型分别如下:




  该函数如调用成功,则返回一个非0值,表明系统中已经注册了一个名为easywin的窗口类。如果失败,则返回0。




  创建窗口 


  当窗口类注册完毕之后,并不会有窗口显示出来,因为注册的过程仅仅是为创建窗口所做的准备工作。实际创建一个窗口的是通过调用createwindow()函数完成的。窗口类中已经预先定义了窗口的一般属性,而createwindow()中的参数可以进一步指定一个窗口的更具体的属性,在easywin程序中,是如下调用createwindow()函数来创建窗口的: 




  createwindow()函数的参数的含义在上面的注释中已有介绍,注释后打了星号标记的参数应该着重注意,其它的参数都很简单,不多做介绍,可参看vc的帮助。


  第一个参数是创建该窗口所使用的窗口类的名称,注意这个名称应与前面所注册的窗口类的名称一致。


  第三个参数为创建的窗口的风格,下表列出了常用的窗口风格:




  程序中使用了ws_overlappedwindow标志,它是创建一个普通窗口常用的标志。而在directx编程中,我们常用的是ws_popup,用这个标志创建的窗口没有标题栏和系统菜单,如果设定窗口为最大化,客户区可以占满整个屏幕,以满足directx编程的需要。


  createwindow()函数后面的参数中,仍用到了该应用程序的实例句柄hinstance。


  如果窗口创建成功,返回值是新窗口的句柄,否则返回null。




  显示和更新窗口 


  窗口创建后,并不会在屏幕上显示出来,要真正把窗口显示在屏幕上,还得使用showwindow()函数,其原型如下: 




  参数hwnd指定要显示得窗口的句柄,ncmdshow表示窗口的显示方式,这里指定为从winmain()函数的ncmdshow所传递而来的值。


  由于showwindow()函数的执行优先级不高,所以当系统正忙着执行其它的任务时,窗口不会立即显示出来,此时,调用updatewindow()函数以可以立即显示窗口。其函数原型如下:




  消息循环 


  在win32编程中,消息循环是相当重要的一个概念,看似很难,但是使用起来却是非常简单。在winmain()函数中,调用initwindow()函数成功的创建了应用程序主窗口之后,就要启动消息循环,其代码如下:




  windows应用程序可以接收以各种形式输入的信息,这包括键盘、鼠标动作 、记时器产生的消息,也可以是其它应用程序发来的消息等等。windows系统自动监控所有的输入设备,并将其消息放入该应用程序的消息队列中。


  getmessage()函数则是用来从应用程序的消息队列中按照先进先出的原则将这些消息一个个的取出来,放进一个msg结构中去。getmessage()函数原型如下:




  getmessage()将获取的消息复制到一个msg结构中。如果队列中没有任何消息,getmessage()函数将一直空闲直到队列中又有消息时再返回。如果队列中已有消息,它将取出一个后返回。msg结构包含了一条windows消息的完整信息,其定义如下:




  该结构中的主消息表明了消息的类型,例如是键盘消息还是鼠标消息等,副消息的含义则依赖于主消息值,例如:如果主消息是键盘消息,那么副消息中则存储了是键盘的哪个具体键的信息。


  getmessage()函数还可以过滤消息,它的第二个参数是用来指定从哪个窗口的消息队列中获取消息,其它窗口的消息将被过滤掉。如果该参数为null,则getmessage()从该应用程序线程的所有窗口的消息队列中获取消息。


  第三个和第四个参数是用来过滤msg结构中主消息值的,主消息值在wmsgfiltermin和wmsgfiltermax之外的消息将被过滤掉。如果这两个参数为0,则表示接收所有消息。


  当且仅当getmessage()函数在获取到wm_quit消息后,将返回0值,于是程序退出消息循环。


  translatemessage()函数的作用是把虚拟键消息转换到字符消息,以满足键盘输入的需要。dispatchmessage()函数所完成的工作是把当前的消息发送到对应的窗口过程中去。


  开启消息循环其实是很简单的一个步骤,几乎所有的程序都是按照easywin的这个方法。你完全不必去深究这些函数的作用,只是简单的照抄就可以了。 
 


  消息处理函数 


  消息处理函数又叫窗口过程,在这个函数中,不同的消息将用switch语句分配到不同的处理程序中去。windows的消息处理函数都有一个确定的样式,即这种函数的参数个数和类型以及其返回值的类型都有明确的规定。在vc的说明书中,消息处理函数的原型是这样定义的:




  如果你的程序中还有其它的消息处理函数,也都必须按照上面的这个样式来定义,但函数名称可以随便取。easywin中的winproc()函数就是这样一个典型的消息处理函数。


  消息处理函数的四个参数是由getmessage()函数从消息队列中获得msg结构,然后分解后得到的。第二个参数umsg和msg结构中的message值是一致的,代表了主消息值。程序中用switch语句来将不同类型的消息分配到不同的处理程序中去。


  winproc()函数明确的处理了4个消息,分别是wm_keydown(击键消息)、wm_rbuttondown(鼠标右键按下消息)、wm_paint(窗口重画消息)、wm_destroy(销毁窗口消息)。


  值得注意的是,应用程序发送到窗口的消息远远不止以上这几条,象wm_size、wm_minimize、wm_create、wm_move等这样频频使用的消息就有几十条。为了减轻编程的负担,windows的api提供了defwindowproc()函数来处理这些最常用的消息,调用了这个函数后,这些消息将按照系统默认的方式得到处理。


  因此,在switch_case语句中,只须明确的处理那些有必要进行特别响应的消息,把其余的消息交给defwindowproc()函数来处理,是一种明智的选择,也是你必须做的一件事。 
 
  结束消息循环 


  当用户按alt+f4或单击窗口右上角的退出按钮,系统就向应用程序发送一条wm_destroy的消息。在处理此消息时,调用了postquitmessage()函数,该函数会给窗口的消息队列中发送一条wm_quit的消息。在消息循环中,getmessage()函数一旦检索到这条消息,就会返回false,从而结束消息循环,随后,程序也结束。 


  小结 


  本章介绍的是win32编程的基础知识,在进行directx编程之前,掌握它们是十分必要的。


  通过本文的学习,你应该学到以下知识:


   如何创建一个win32应用程序工程 


   用registerclass()函数注册一个窗口类,再立即调用createwindow()函数创建一个窗口的实例 


   设置窗口的类型以及将一个消息处理函数与窗口联系上 


   用一固定的模式开启消息循环 


   了解消息处理函数的定义规则,如何自己定义一个窗口消息处理函数 


   在消息处理函数中,最后必须调用defwindowproc()函数以处理那些缺省的消息 


   调用postquitmessage()函数以结束消息循环 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
c++面试题53个问题 1.C++的三大特性 2.C和C++的区别 3.全局变量和局部变量在内存分配上有何不同 4.static的作用 5.const解释其作用 6.指针和引用的区别 7.智能指针 8.简述深拷贝和浅拷贝的区别 9.编my_strcpy函数,实现与库函数strcpy似的功能,不能使用任何库函数 10.请讲述堆和栈的区别 11.全局变量和局部变量有什么区别?实怎么实现的?操作系统和编译器是怎么知道的 12.new、delete、malloc、free之间的关系 13. 头文件种的ifndef/define/endif 是干什么用的 14.TCP和UDP有什么区别 15.STL库用过吗?常见的STL容器有哪些?算法用过哪几个 16.同步IO和异步IO的区别 17.说下你对内存的了解 18.C++文件编译与执行的四个阶段 19.extern关键字的作用 20.#define和const的区别 21.结构体struct和共同体union(联合)的区别 22.C++中vector和list的区别 23.结构体和的区别 24.STL中map和set的原理(关联式容器) 25.MFC的消息机制 26.消息映射 27.列举几种进程的同步机制,并比较其优缺点 28.数组和链表的区别 29.MFC主要要用到哪几个?及其各个的作用 30.MFC六大核心机制 31.OnDraw和OnPaint 32.win32程序的消息响应机制是如何实现的 33.MFC中的消息响应机制是如何实现的 34.WM_COMMAND命令消息处理顺序 35.MFC序列化的概念 36.PeekMessage和GetMessage的主要区别 37.WIN32创建窗口程序基本函数 38.Windows中的系统消息循环占用CPU的疑问 39.队列消息与非队列消息 40.GDI对象绘图步骤 41.设备上下文DC 42.GDI位图绘制步骤 43.当模态对话框点开后,主窗口还能响应处理消息吗 44.MFC的消息 45.CListCtrl 虚拟列表技术 46.虚函数是怎么实现的 47.什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法? 48.变量的声明和定义有什么区别 49.sizeof是一个操作符,strlen是库函数 50.一个“标准”宏MIN 51.简述strcpy sprintf与mencpy的区别 52.链表与数组的区别 53.简述队列和栈的异同
什么是句柄? 句柄,是整个Windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息,但是句柄不是一个指针,程序不能利用句柄来直接阅读文件中的信息。如果句柄不用在I/O文件中,它是毫无用处的。 句柄是Windows用来标志应用程序中建立的或是使用的唯一整数,Windows使用了大量的句柄来标志很多对象。 一、MFC AppWizard 1、MFC(Microsoft Foundation Class,微软基础库)是微软为了简化程序员的开发工作所开发的一套C++ 的集合,是一套面向对象的函数库,以的方式提供给用户使用 2、MFC AppWizard是一个辅助我们生成源代码的向导工具,它可以帮助我们自动生成基于MFC框架的源代码 二、基于MFC的程序框架剖析 1、MFC程序的ClassView标签页(图) 2、继承关系 (1)CMainFrame继承于CFrameWnd (2)CTestApp继承于CWinApp (3)CTestDoc继承于CDocument (4)CTestView继承于CView 注:CFrameWnd和CView都继承于CWnd 3、CWnd是MFC中一个非常重要的,它封装了与窗口相关的操作 4、MFC的简化组织结构图(图) 5、MFC程序也有一个WinMain函数,程序是在编译时,由链接器将它链接到程序中 6、MFC程序具有一个CTestApp的全局对象theApp,在MFC程序运行时,程序执行的顺序为:theApp全局对象定义 处->CTestApp构造函数->WinMain函数 7、对于普通的VC++控制台程序,无论全局变量还是全局对象,程序运行时,在加载main函数之前,就已经为它们 分配了内存空间。对于一个全局对象来说,此时就会调用该对象的构造函数,构造该对象,并进行初始化操作 8、实例句柄与全局对象 (1)对于Win32 SDK程序,应用程序的实例是由实例句柄(WinMain函数的hInstance参数)来标识的 (2)对于MFC程序,应用程序的实例是由全局对象(每一个MFC程序有且仅有一个从应用程序CWinApp派生的, 如CTestApp,它实例化theApp全局对象)来标识的 9、基构造函数中this指针的指向问题 在构造子对象时,会自动调用父的构造函数,此时在父的构造函数中的this指针所指向的是子对象地址 10、AfxWinMain函数 MFC程序的WinMain函数是通过调用AfxWinMain函数来完成它的功能的 注:Afx前缀的函数代表应用程序框架(Application Framework)函数,它们可以在程序的任何地方被调用 11、CTestApp::InitInstance函数 在AfxWinMain函数中,通过调用InitInstance函数来完成MFC内部管理方面的工作 12、AfxEndDeferRegisterClass函数 MFC提供了一些默认的标准窗口,我们只需要选择所需的窗口就行。然后,调用AfxEndDeferRegisterClass 函数来注册窗口 13、CMainFrame::PreCreateWindow函数 MFC程序具有两个窗口(框架窗口和视窗口),在框架窗口产生之前会调用PreCreateWindow函数 14、CWnd::CreateEx函数 在MFC程序中,窗口创建是由CreateEx函数实现的 15、CWnd::CreateWindowEx函数 主要作用是当修改了CreateEx函数的CREATESTRUCT参数时,CreateWindowEx函数会根据参数发生的相应变化来创 建一个符合我们要求的窗口 注:MFC中后缀名为Ex的函数都是扩展函数 16、CMainFrame::ShowWindow函数和CMainFrame::UpdateWindow函数 用于显示应用程序框架窗口和更新这个窗口 17、CWinThread::Run函数和CWinThread::PumpMessage函数 用于完成消息循环 18、DefWindowProc函数 默认的窗口过程,但MFC程序对消息的处理实际上是通过消息映射机制来完成的 19、MFC程序的运行过程 (1)首先利用全局应用程序对象theApp启动应用程序 (2)调用全局应用程序对象的构造函数,从而就会调用其基CWinApp的构造函数,以完成应用程序的一些初始化 (3)进入WinMain函数 (4)进入消息循环 20、MFC程序的主要过程 theApp-> CTestApp::CTestApp构造函数-> CWinApp::CWinApp构造函数-> _tWinMain(WinMain函数的宏)-> AfxWinMain函数-> CTestApp::InitInstance函数-> AfxEndDeferRegisterClass函数-> CMainFrame::PreCreateWindow函数-> CFrameWnd::PreCreateWindow函数-> AfxDeferRegisterClass(AfxEndDeferRegisterClass函数的宏)-> CFrameWnd::Create函数-> CWnd::CreateEx函数-> CMainFrame::PreCreateWindow函数-> CWnd::CreateEx函数-> CMainFrame::ShowWindow函数-> CMainFrame::UpdateWindow函数-> CWinThread::Run函数-> CWinThread::PumpMessage函数 21、框架窗口(整个应用程序外框所包括的部分)是视窗口(框架窗口中空白的地方)的一个窗口 22、MFC提供了一个文档/视的结构,文档是指CDocument,视是指CView。前者用于数据的存储和加载, 后者用于数据的显示和修改 23、框架对象、文档对象和视对象是通过一个单文档模板指针来有机地组织在一起,并利用AddDocTemplate函数 把这个单文档模板添加到文档模板中,从而把这三个组织成为一个整体 24、MFC程序的CAboutDlg继承于CDialog,用于为用户提供一些与程序相关的帮助信息 三、窗口窗口对象与窗口 1、以“::”开始的函数是一个全局函数,表示调用的是Platform SDK的函数 2、如果我们关闭了一个窗口,这个窗口就销毁了,那么该窗口对应的C++窗口对象销毁了吗? (1)当一个窗口销毁时,它会调用CWnd::DestroyWindow函数,该函数销毁窗口后,将CWnd::m_hWnd设为NULL (2)窗口的生命周期和C++窗口对象的声明周期不是一致的。当一个窗口销毁时,与C++窗口对象没有关系,它 们之间的纽带仅仅在于这个C++窗口内部的成员变量m_hWnd,该变量保存了与这个C++窗口对象相关的哪个窗口 的句柄 (3)但是,当C++窗口对象销毁时,与之相关的窗口也将销毁,因为它们之间的纽带m_hWnd已经断了 3、示例---在窗口中显示按钮 (1)CButton按钮继承于CWnd (2)对于一个CButton对象,在定义之后就可以使用了;但是,如果要显示这个按钮的话,还需调用 CButton::Create函数,把按钮窗口与CButton对象关联起来 (3)MFC程序的窗口创建时都会产生WM_CREATE消息,该消息通过OnCreate函数来捕获。对于框架窗口来说,MFC直 接把OnCreate函数提供到了CMainFrame中;而在视窗口中没有提供该函数,如需使用,要用户自行添加 (4)通常对MFC程序的操作,都是在CTestView视窗口中进行的 (5)在窗口创建之后,要显示该窗口可以通过调用ShowWindow函数或指定窗口风格为WS_VISIBLE来实现 (6)实现过程 A:在CTestView中,添加CButton型的私有成员m_btn B:在CTestView中,添加WM_CREATE消息的OnCreate处理函数 C:在CTestView中,通过GetParent函数可以获得CMainFrame框架窗口对象的指针 D:实现一(在视窗口中通过ShowWindow函数显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); m_btn.ShowWindow(SW_SHOWNORMAL); return 0: } E:实现二(在视窗口中通过WS_VISIBLE风格显示窗口) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); return 0: } F:实现三(在框架窗口中显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),GetParent(),123); return 0: } 即便是基于MFC的应用程序,建立窗口也是会遵循如下的过程: 设计窗口->注册窗口->生成窗口->显示窗口->更新窗口->消息循环->消息路由到窗口过程函数处理。下面就剖析一下在MFC中是如何完成上述过程的。 (1)每个应用程序都有且仅有一个应用的全局变量theApp,全局变量先于WinMain函数进行处理。 (2)WinMain函数体在APPMODUL.CPP文件中,定义如下: extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 其中#define _tWinMain WinMain (3)AfxWinMain函数体在WINMAIN.CPP文件中,里面有如下两句话: CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); 其实这里得到的这两个指针都是指向全局的对象theApp的; 接下来有函数调用pThread->InitInstance(),根据多态性,会调用CXXXApp中的InitInstance()函数。该函数很重要,在对该函数的调用中就会完成:设计窗口->注册窗口->生成窗口->显示窗口->更新窗口。 接下来,该函数中会继续调用pThread->Run(),这就完成了:消息循环->消息路由到窗口过程函数处理。 (4)进入CXXXApp::InitInstance()函数体中,对于单文档应用程序,调用ProcessShellCommand(cmdInfo),通过调用该函数就会完成:设计窗口->注册窗口->生成窗口。 再接下来就会调用m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();这就完成了:显示窗口->更新窗口。 (5)在函数CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)中会进入到如下的case分支:case CCommandLineInfo::FileNew: if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)) (6)进入函数CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo),调用_AfxDispatchCmdMsg(this, nID, nCode, lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo); (7)进入函数AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode, AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),调用 case AfxSig_vv: // normal command or control notification ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED ASSERT(pExtra == NULL); (pTarget->*mmf.pfn_COMMAND)(); (8)进入CWinApp::OnFileNew(),调用m_pDocManager->OnFileNew();这个函数很特殊,它本身是个消息响应函数,当我们点击ID为ID_FILE_NEW的菜单时,会产生一个命令消息,由于命令消息可以被CCmdTarget及其派生来捕获,而CWinApp是从CCmdTarget派生出来的,因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜单下的新建菜单项时,就会首先进入这个函数,然后再依次执行下去,最后就会执行到pDocument->OnNewDocument()中,往往我们会对这个函数不解,不知道它为什么会响应ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜单项,为什么会自动的产生这个消息呢?这是因为在CXXXXApp::InitInstance()函数中有“CCommandLineInfo cmdInfo;”这个的构造函数是这样的:CCommandLineInfo::CCommandLineInfo() { m_bShowSplash = TRUE; m_bRunEmbedded = FALSE; m_bRunAutomated = FALSE; m_nShellCommand = FileNew; },因此就会在第(5)步骤的时候进入到“case CCommandLineInfo::FileNew:”这个分支中,就相当于产生了这样一个FileNew的消息。同理对于ID为ID_FILE_OPEN(在CWinApp::OnFileOpen()中响应)、ID_FILE_SAVE(在CDocument::OnFileSave()中响应)等等在MFC向导为我们生成的单文档中找不到消息响应的入口时,其实都是在基CWinApp或者CDocument中进行了响应。对于CXXXXDoc::Serialize(CArchive& ar)函数也是通过ID_FILE_SAVE和ID_FILE_OPEN产生命令消息后就行响应从而才调用该函数的。 (9)进入CDocManager::OnFileNew(),CDocManager一个成员变量是CPtrList m_templateList;该变量保存了一个文档模版链表指针,在CDocManager::OnFileNew()函数体中会调用CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();得到链表中的头,也就是第一个文档模版,后面就会用得到的这个指针去调用pTemplate->OpenDocumentFile(NULL);紧接着就会有一个判断,用来确定该链表中是否只有一项,如果链表中保存了多个文档模版,则会弹出一个对话框,来让我们选择到底是使用哪一套文档模版来构建应用程序,相信大家也都见到过这种情况吧。对了,还有一点要说明的是:pTemplate是一个CDocTemplate的指针,但接下来程序为什么会进入到CSingleDocTemplate::OpenDocumentFile的函数体内呢,这是因为CDocTemplate中的OpenDocumentFile函数被定义为纯虚函数,而CSingleDocTemplate又是从CDocTemplate派生出来的,并且实现了该函数,因此就会进入到子的函数体中了。 (10)进入CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible),先调用CreateNewDocument()创建文档,再调用pFrame = CreateNewFrame(pDocument, NULL);创建框架和视图,从这里也可以看出MFC体系结构中文档、框架、视图“三位一体”的模式,在这一个函数中同时创建三个;再会调用pDocument->OnNewDocument();因此就会进入到子的文档中的pDocument->OnNewDocument()中了。 (11)进入CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther),调用if (!pFrame->LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, // default frame styles NULL, &context)) (12)进入BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); (13)进入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),该函数内部就完成了:设计窗口->注册窗口。MFC通过给我们提供好一些已经订制好的窗口,我们不需要自己再设计窗口,只需要到那些订制好的窗口“仓库”中寻找一种适合我们需要的窗口就可以了,然后通过AfxRegisterClass函数注册窗口。还需要说明的是,再后续的跟踪过程中,我们会发现还会进入到AfxEndDeferRegisterClass函数中进行设计和注册窗口,这主要是因为单文档应用程序比较特殊,它提前通过这样的一种途径进行了窗口的设计和注册步骤,其实是应该在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函数的调用中完成窗口的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口的正宗发源地应该是PreCreateWindow(CREATESTRUCT& cs)。此外,我们还会注意到在该函数体的前部分有一语句为“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口窗口过程函数都是DefWindowProc,这一点在后面的跟踪中可以看到,每次生成窗口之后都会调用几次DefWindowProc函数。也就是说MFC都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消息处理了,MFC采用了一种基于消息映射的机制完成了消息个性化处理。 (14)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)中,调用LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource); (15)进入LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource),调用PreCreateWindow(cs); (16)进入BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs),调用CFrameWnd::PreCreateWindow(cs) (17)进入BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));又一次设计和注册窗口 (18)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)中,调用if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)) (19)进入BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext),调用if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext)) (20)BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),调用if (!PreCreateWindow(cs)) ,接下来调用HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);好了,终于让我们找到生成窗口的地方了——函数::CreateWindowEx! (21)进入int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),调用if (CFrameWnd::OnCreate(lpCreateStruct) == -1) (22)进入int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs),调用return OnCreateHelper(lpcs, pContext); (23)进入int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (CWnd::OnCreate(lpcs) == -1) (24)进入_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT),调用return (int)Default(); (25)进入LRESULT CWnd::Default(),调用return DefWindowProc(pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam); (26)进入LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),调用return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam); (27)回到int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (!OnCreateClient(lpcs, pContext)) (28)进入BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext),调用if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL) (29)进入CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID),调用if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext)) (30)进入BOOL CWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext),调用return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext); (31)进入BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),重复生成框架CMainFrame的过程来生成CXXXView,因为它也是一个窗口,因此也需要进行那一系列过程才能最终显示更新出来。 调用的顺序是这个样子的:PreCreateWindow(cs)->BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)->CView::PreCreateWindow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));->::CreateWindowEx(...)->CWnd::DefWindowProc->::CallWindowProc(...)->...->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->... 到这里,基本上就清楚了,中间的省略号表示的部分大多数都是在与窗口过程函数有关的,因为在生成窗口的时候需要响应一些消息,因此需要调用一些窗口过程函数,每次在调用::CreateWindowEx(...)函数后都会调用一些窗口过程函数,然后再去调用该窗口对应的OnCreate函数,其实在调用OnCreate函数之前调用CreateWindowEx只是生成了一个窗口,至于这个窗口里面要放置些什么东西,以及该如何装饰该窗口,则就需要由OnCreate来完成了,往往我们都会在OnCreate函数的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东西,比如我们通常会在CMainFrame的OnCreate函数后面放置一些Create代码,来创建我们自己的可停靠的工具栏或者按钮之的东西,当然我们也可以在CXXXView的OnCreate函数的后面添加一些代码,来创建我们需要的东西,比如按钮之的东西。在完成了从设计、注册到生成窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的调用CWnd的ShowWindow和UpdateWindow两个函数,我们可以在创建的时候,给窗口风格中添加WS_VISIBLE即可,因此有些时候会跟踪不到ShowWindow和UpdateWindow两个函数这两个函数,因为窗口创建的时候就可见了。 总的来说,先初始化应用,然后注册生成框架,然后再注册生成视图,然后注册生成视图OnCreate函数后面用户添加的、用Create来准备创建窗口,然后再注册生成框架的OnCreate函数后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的要创建窗口,最后在回到应用的初始化的函数体中,调用框架的显示和更新函数,然后再进入由框架定义的窗口消息循环中。 消息循环的过程是这个样子的: (1)调用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)函数中的pThread->Run() (2)进入int CWinApp::Run(),调用return CWinThread::Run(); (3)进入int CWinThread::Run(),调用if (!PumpMessage()) (4)进入BOOL CWinThread::PumpMessage(),调用if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) (5)回到BOOL CWinThread::PumpMessage(),调用::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur); (6)回到int CWinThread::Run(),调用while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); (7)再重复(4)-(6)的步骤 下面给出int CWinThread::Run()中消息循环的部分代码: do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); 这段代码其实本质上与我们基于Win32 SDK手的代码: //消息循环 MSG msg; while(GetMessage(&msg,NULL,0,0)) { //简单的说,函数TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没有该函数就不能产生WM_CHAR消息。 TranslateMessage(&msg); ::DispatchMessage(&msg); } 是一致的。 1,寻找WinMain人口: 在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。 路径:MFC|SRC|APPMODUL.CPP: _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 注意:(#define _tWinMain WinMain) 2,对于全局对象或全局变量来说,在程序运行即WINMAIN函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。 所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){} 说明:每一个MFC程序,有且只有一个WinApp派生的(应用程序),也只有一个从应用程序所事例化的对象,表示应用程序本身。在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。 3,通过构造应用程序对象过程中调用基CWinApp的构造函数,在CWinApp的构造函数中对程序包括运行时一些初始化工作完成了。 CWinApp构造函数:MFC|SRC|APPCORE.CPP CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参数,而CTEApp构造函数没有显式向父传参,难道CWinApp()有默认参数?见下: (在CWinApp定义中, CWinApp(LPCTSTR lpszAppName = NULL); ) 注意:CWinApp()函数中: pThreadState->m_pCurrentWinThread = this; pModuleState->m_pCurrentWinApp = this (this指向的是派生CTEApp对象,即theApp) 调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){} 4,_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些做一些有机的集成,我们可根据这些函数来设计自己的应用程序)。 AfxWinMain()函数路径:MFC|SRC|WINMAIN.CPP: 在AfxWinMain()函数中: CWinApp* pApp = AfxGetApp(); 说明:pApp存储的是指向WinApp派生对象(theApp)的指针。 //_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() // { return afxCurrentWinApp; } 调用pThread->InitInstance() 说明:pThread也指向theApp,由于基中virtual BOOL InitApplication()定义为虚函数,所以调用pThread->InitInstance()时候,调用的是派生CTEApp的InitInstance()函数。 nReturnCode = pThread->Run(); 说明:pThread->Run()完成了消息循环。 5,注册窗口:AfxEndDeferRegisterClass(); AfxEndDeferRegisterClass()函数所在文件:MFC|SRC|APPCORE.CPP BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...} 说明:设计窗口:在MFC中事先设计好了几种缺省的窗口,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口。 调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}//进入程序 ->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()//父InitInstance虚函数;->CTEApp::InitInstance()//子实现函数;->AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口(出于文档管理,注册提前,正常的应在PreCreateWindow中进行注册)//之后进入创建窗口阶段(以下再不做调试) 6,PreCreateWindow()://主要是注册窗口 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; return TRUE; } 说明: CFrameWnd::PreCreateWindow()函数所在文件:MFC|SRC|WINFRM.CPP BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) { if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); //判断AFX_WNDFRAMEORVIEW_REG型号窗口是否注册,如果没有注册则注册 cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background //把注册后的窗口名赋给cs.lpszClass } if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4) cs.style |= FWS_PREFIXTITLE; if (afxData.bWin4) cs.dwExStyle |= WS_EX_CLIENTEDGE; return TRUE; } 其中: virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是个虚函数,如果子有则调用子的。 #define VERIFY(f) ASSERT(f) #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass) define AFX_WNDFRAMEORVIEW_REG 0x00008 const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件中,定义为全局数组。 //#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView") 7,创建窗口: Create()函数路径:MFC|SRC|WINFRM.CPP: CFrameWnd::Create(...){ ... CreateEx(...);//从父继承来的,调用CWnd::CreateEx(). ... } CWnd::CreateEx()函数路径:MFC|SRC|WINCORE.CPP BOOL CWnd::CreateEx(...){ ... if (!PreCreateWindow(cs))//虚函数,如果子有调用子的。 { PostNcDestroy(); return FALSE; } ... HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); ... } 说明:CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数,如果子有调用子的。 HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); typedef struct tagCREATESTRUCT { // cs LPVOID lpCreateParams; HINSTANCE hInstance; HMENU hMenu; HWND hwndParent; int cy; int cx; int y; int x; LONG style; LPCTSTR lpszName; LPCTSTR lpszClass; DWORD dwExStyle; } CREATESTRUCT; 8,显示和更新窗口: CTEApp,TEApp.cpp中 m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口 m_pMainWnd->UpdateWindow();//更新窗口 说明: class CTEApp : public CWinApp{...} class CWinApp : public CWinThread{...} class CWinThread : public CCmdTarget { ... public: CWnd* m_pMainWnd; ... ... } 9,消息循环: int AFXAPI AfxWinMain() { ... // Perform specific initializations if (!pThread->InitInstance()){...} //完成窗口初始化工作,完成窗口的注册,完成窗口创建,显示和更新。 nReturnCode = pThread->Run(); //继承基Run()方法,调用CWinThread::Run()来完成消息循环 ... } //////////////////////////////////////////////////////////////// CWinThread::Run()方法路径:MFC|SRC|THRDCORE.CPP int CWinThread::Run() { ... // phase2: pump messages while available do//消息循环 { // pump message, but quit on WM_QUIT if (!PumpMessage())//取消息并处理 return ExitInstance(); ... } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); ... } 说明: BOOL PeekMessage(,,,,)函数说明 The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure. If a message is available, the return value is nonzero. If no messages are available, the return value is zero. ///////////////////////////////////////////////////////////// BOOL CWinThread::PumpMessage() { ... if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息 {...} ... // process this message if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { ::TranslateMessage(&m_msgCur);//进行消息(如键盘消息)转换 ::DispatchMessage(&m_msgCur);//分派消息窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。) } return TRUE; } 9,文档与视结构: 可以认为View窗口是CMainFram窗口的子窗口。 DOCument是文档。 DOC-VIEW结构将数据本身与它的显示分离开。 文档:数据的存储,加载 视:数据的显示,修改 10,文档,视,框架的有机结合: 在CTEAppCTEApp::InitInstance()函数中通过文档模板将文档,视,框架的有机组织一起。 ... CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTEDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CTEView)); AddDocTemplate(pDocTemplate);//增加到模板
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值