您的位置:首页 > 移动开发

VC++孙鑫-第三章-MFC AppWizard的原理与MFC程序框架-讲WIN32SDK和MFC编程的相同过程

2018-01-16 10:02 701 查看
2013年1月17日更新-2014年12月3日-2014年12月9日-本章主要是讲MFC的程序和WIN 32SDK程序的相同之处,其实都是包括设计窗口类,注册窗口类,创建窗口,显示和更新窗口,消息循环,以及窗口过程函数。MFC的程序过程在孙鑫的P87也有总结,下次直接看总结就好了,不用再看视频了。

在MFC中,类的命名都以字母“C”开头。CAboutDlg类派生于CDialog类,CTestView类派生于CView类,CMainFrame类派生于CFrameWnd类,而CFrameWnd是由CWnd派生的,CView类也是由CWnd类派生的,CWnd类封装了于窗口相关的操作。每一个类都有一个基类。F9键设置断点,F5键调试运行当前程序MFC的入口函数在哪?winmain函数是所有函数的入口函数,C:\Program
Files\Microsoft Visual Studio\VC98\MFC\SRC SRC下面有MFC的源代码,APPMODUL.CPP文件下面有一个WinMain函数,可以找到下面的WinMain函数

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)
{
 // call shared/exported WinMain
 return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
设置断点后,发现程序在编译连接时,确实要经过_tWinMain,
WinMain函数前面有一个_t, 可以go to definition看到#define _tWinMain   WinMain  说明 _tWinMain 是一个宏,就是WinMain. #define命令将一个指定的标示符(即宏名)来代表一个字符串
那么MFC的这些类(CAboutDlg,CTestView,CMainFrame,CTestApp,CTestDoc)是如何和WinMain函数关联的?
无论是全局变量还是全局对象,程序在运行时,在加载main函数之前,就已经为全局变量或者全局对象分配了内存空间。对于一个全局对象来说,此时就会调用该对象的构造函数,构造该对象,并进行初始化操作。

首先需要了解MFC程序的流程顺序,程序首先到CTestApp
theApp(定义类的对象,类名 对象名)这里定义全局对象theApp,然后需要运行派生类CTestApp的构造函数,
the App全局对象表示了应用程序本身,每个MFC程序有且仅有一个从应用程序类(CWinApp)派生的类,每个MFC程序有且仅有一个该派生类实例化的对象,就是the App

但是在运行子类的构造函数前首先需要运行基类CWinApp的构造函数CWinApp()
CWinApp:CWinApp(LPCTSTR lpszAppName) // 知识点:(1)在类外定义构造函数;(2)构造函数的名字和类名相同
{
---------
pModuleState->m_pCurrentWinApp==this;这里this对象代表的是子类CTestApp的对象(the App) pModuleState所指向的对象中的m_pCurrentWinApp成员。
----------
}
当程序调用的CWinApp的构造函数,并执行了CTestApp的构造函数,并且产生了theApp对象后,接下来就是进入WinMain函数,这里WinMain函数是通过AfxWinMain实现的,前缀Afx--代表应用程序框架函数。
int AFXAFI AfxWinMain()
{
CWinThread* pThread=AfxGetThread(); //
MSDN中查找,AfxGetThread()是指向当前正在执行的线程的指针,知识点,定义pThread为指向CWinThread类对象的指针变量
CWinApp* pApp=AfxGetApp();// 获取指向当前应用程序的指针,也就是CWinApp的派生类CTestApp的指针,也就是theApp指针,就是上面的this指针
pApp和pThread所指向的实际上是一样,就是派生类CTestApp的指针。
if (pApp!=NULL && !pApp->InitApplication()) //如果pApp为非空,并且pApp所指向的的对象(the)的成员函数InitApplication()为非真时,则到goto
InitFailure InitApplication完成MFC内部管理方面的工作。
goto InitFailure

if(!pThread->InitInstance()) //接着调用pThread所指向对象(The
App)的成员函数InitInstance()

nReturnCode = pThread->Run();
}
通过AfxGetThread()这个函数中的代码
if (pThread==NULL)
     pThread=AfxGetApp();
Return pThread; 所以AfxGetThread()返回的就是AfxGetApp()的结果
AfxGetApp()这个函数的代码如下
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
{return afxCurrentWinApp;}
而afxCurrentWinApp的定义如下
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp  //这里m_pCurrentWinApp==this指针,
结论就是AfxGetApp得到的是CWinApp中保存的this指针,这个this指针实际上指向the App这个全局对象。也就是说pThread和pApp都是指向CTestApp类的全局对象the
App指针。

InitInstance()后就是注册窗口类,系统已经预设定了一些窗口类,只要选择所需要的窗口类,然后注册就可以了。窗口类的注册是由AfxEndDeferRegisterClass函数来完成的。单文档程序有两个窗口,其中一个是框架窗口,另一个View类窗口。然后PreCreateWindow.然后就是创建窗口,在MFC中创建窗口是由CWin类的CreatEx函数实现的。
m_pMainWnd->ShowWindow(SH_SHOW);  显示应用程序框架窗口 m_pMainWnd 指针类型 指向框架窗口的指针,也就是指向CMainFrame对象的指针
m_pMainWnd->UpdateWindow();  更新应用程序框架窗口

最后就是消息循环
int CWinThread::Run()
{
    if(!PumpMessage())
}

BOOL CWinThread::PumpMessage()
{
    if(!::GetMessage(&m_msgCur,NULL,NULL,NULL)
{

}

if()
{
       ::TranslateMessage(&m_msgCur);
       ::DispatchMessage(&m_msgCur);
}
}

wndcls.lpfnWndProc = DefWindowProc; 这行代码的作用是窗口过程函数。

 pDocTemplate = new CSingleDocTemplate(
  IDR_MAINFRAME,
  RUNTIME_CLASS(CTestDoc),
  RUNTIME_CLASS(CMainFrame),       // main SDI frame window
  RUNTIME_CLASS(CTestView));
 AddDocTemplate(pDocTemplate);

这段函数把文档类CTestDoc和视类CMainFrame和CTestView组合在一起。
CAboutDlg类也是一个窗口类,该类是一个无关紧要的类。

窗口类,窗口类对象和窗口的关系
窗口对象和窗口的联系是,窗口对象中保存了和窗口相联系的窗口句柄。当窗口消失的时候窗口对象的生命周期并没有结束。

  
   单文档应用程序都会有CxxxxApp类(是从CWinApp派生来的,表示是一个应用程序类),CxxxxDoc类(文档类),CxxxxView类,CMainFrame框架类,CMainFrame类名字是不会变的,程序运行的时候先运行CxxxxApp
theApp定义theApp这个全局变量(对于全局变量来说,在运行winmain函数钱就已经给全局变量赋值,分配内存空间,在执行winmain函数之前就已经分配内存空间,用theApp这个对象来表示这个应用程序),由于定义了全局变量theApp所以要调用CxxxxApp()这个构造函数来给theApp赋值,然后再运行到_tWinMain函数。在APPCORE.CPP文件中可以找到CWinAPP函数,如下:
CWinApp::CWinApp(LPCTSTR lpszAppName) ,可以看到他的构造函数带了一个形参,但是CxxxxApp::CxxxxApp()里面是没有参数的,如果参数有一个缺省值,这样就不用传递形参。pThreadState->m_pCurrentWinThread
= this 这里面的this指针指向theApp这个对象。 然后调用InitInstance(),然后注册窗口类,来完成设计窗口类,先创建CMainFrame是框架窗口,然后创建CxxxxView是显示窗口

::函数,表示这个一个WIN 32 平台SDK的函数。如果函数名不同那么也可以不写::,如果函数名和MFC中的函数相同那么需要加上::。


class CWnd
{
public:
 BOOL CreateEx(DWORD dwExStyle,      // extended window style
    LPCTSTR lpClassName,  // registered class name
    LPCTSTR lpWindowName, // window name
    DWORD dwStyle,        // window style
    int x,                // horizontal position of window
    int y,                // vertical position of window
    int nWidth,           // window width
    int nHeight,          // window height
    HWND hWndParent,      // handle to parent or owner window
    HMENU hMenu,          // menu handle or child identifier
    HINSTANCE hInstance,  // handle to application instance
    LPVOID lpParam);        // window-creation data
 BOOL ShowWindow(int nCmdShow);
 BOOL UpdateWindow();
public:
 HWND m_hWnd;
};
BOOL CWnd::CreateEx(DWORD dwExStyle,      // extended window style
    LPCTSTR lpClassName,  // registered class name
    LPCTSTR lpWindowName, // window name
    DWORD dwStyle,        // window style
    int x,                // horizontal position of window
    int y,                // vertical position of window
    int nWidth,           // window width
    int nHeight,          // window height
    HWND hWndParent,      // handle to parent or owner window
    HMENU hMenu,          // menu handle or child identifier
    HINSTANCE hInstance,  // handle to application instance
    LPVOID lpParam)        // window-creation data
{
 m_hWnd=::CreateWindowEx(dwExStyle,lpClassName,dwStyle,x,y,
     nWidth,nHeight,hWndParent,hMenu,hInstance,
     lpParam);
 if(m_hWnd!=NULL)
  return TRUE;
 else
  return FALSE;
}
BOOL CWnd::ShowWindow(int nCmdShow)
{
 return ::ShowWindow(m_hWnd,nCmdShow);  //调用平台SDK的函数
}
BOOL CWnd::UpdateWindow()
{
 return ::UpdateWindow(m_hWnd);
}
int WINAPI WinMain(
  HINSTANCE hInstance,      // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,          // command line
  int nCmdShow              // show state
)
{
 WNDCLASS wndcls;
 wndcls.cbClsExtra=0;
 wndcls.cbWndExtra=0;
 ......
 RegisterClass(&wndcls);
 CWnd wnd;
 wnd.CreateEx(...);
 wnd.ShowWindow(SW_SHOWNORMAL);
 wnd.UpdateWindow();
 HWND hwnd;
 hwnd=CreateWindowEx();
 ::ShowWindow(hwnd,SW_SHOWNORMAL);
 ::UpdateWindow(hwnd);
 ......
}当窗口对象析构的时候,应为操作系统要回收窗口对象的资源,所以窗口也销毁了,但是当窗口销毁的时候,窗口对象并没有销毁。也就是说当窗口销毁的时候,窗口类内部的成员函数是可以被窗口对象所调用的。

孙鑫最后关于CButton类的讲解,在MSDN查看一个类的时候,同时需要查看它的成员函数,里面有构造函数,但是我们要使用这个对象,创建一个按钮的时候,还需要调用它的初始化函数Initialization Create,Create函数的作用是把CButton对象和所创建的windows按钮相联系。

在CMainFrame里面有一个OnCreate函数,用在响应创建框架比如,函数里面有创建工具栏,状态栏。涉及到一个知识点就是在成员函数内部创建的对象,局部的对象,当函数执行完后,操作系统就会回收资源,对象就会被销毁。在类里面增加成员变量的方法

可以看到,标题栏和菜单栏都在非客户区,而工具栏是在客户区的。

在VC++中添加某个消息的处理函数的方法

在单文档对话框中,用GetParent()函数可以获得父窗口的指针,单文档对话框的父窗口就是CMainFrame类对象创建的那个框架窗口。

此外还有一点是关于CWnd::ShowWindow函数和平台SDK中的ShowWindow函数的区别,可以看到CWnd类成员的ShowWindow函数并不需要传递hWnd窗口句柄,应为hWnd是CWnd的成员变量,CWnd::ShowWindow已经默认传递了这个窗口句柄了。

CWnd::ShowWindow
BOOLShowWindow(intnCmdShow);
Return Value
Nonzero if the window was previously visible; 0 if the
CWnd was previously hidden.

ShowWindow
The
ShowWindow function sets the specified window's show state.
BOOL ShowWindow( HWNDhWnd,
// handle to window intnCmdShow
// show state);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: