第3课MFC程序框架之一直接实践【参考资料:孙鑫VC++教学视频】
我们现在用VC6直接实践一下MFC编程:
1、程序类型MFC AppWizard(exe)类型;
2、选择Single Document单文档
3、完成
虽然我们没有写一行代码,但是我们运行后,可以看到已经生成了一个对话框了,标准的对话框;
在class view里面可以看到一些类:
CAoutDlg
cMainFraim
CTestApp
CTestView
CTestDoc
并且可以看到:
class CTestView : public CView
CTestView是从CView类里面派生出来的;
其他的类也是从相应的类里面派生出来的;
那么由编译器自动生成的对话框,有没有WinMain函数呢?
在VC6安装目录下VC98里面MFC里面SRC文件夹里面,
搜索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);
}
这个函数就是我们原来的WinMain函数;
再继续查看:
TCHAR.h里面可以看到
#define _tWinMain WinMain
真相大白,就是我们的WinMain函数;
第3课MFC程序框架之二执行顺序【参考资料:孙鑫VC++教学视频】
在class view里面可以看到一些类:
CAoutDlg
cMainFraim
CTestApp
CTestView
CTestDoc
那么我们的主函数和哪个类有关系,或者说被集成到哪个类里面了?
WinMain函数还是不是程序的入口函数呢?
采用设置断点的方式找到执行顺序!
第一步:全局对象CTestApp theApp; 在Test.cpp【CTestApp类的实现文件】; //全局变量
第二步:CTestApp对象的构造函数;
第三步:_tWinMain函数;
★全局变量或者全局对象要先于main函数之前运行的;
★结论:在MFC程序中,WinMain还是入口函数;
★全局对象CTestApp theApp唯一的表示我们的应用程序;
第3课MFC程序框架之三设计注册创建【参考资料:孙鑫VC++教学视频】
我们来研究WinMain函数:
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
它实际上是调用了AfxWinMain函数;
★Afx是什么意思?
是应用程序框架类的一堆函数,Application Framework;x=没用,是为了美观方便加上去的
是一些全局函数,用在类之间的连接;
可以在每一个类中调用;
在WinMain.cpp源文件里面,我们找到了AfxWinMain函数。
★在这个函数里面完成了设计、注册、创建窗口;
设计窗口时MFC已经设计了一些缺省的窗口,注册时预先已经完成了;
注册窗口的函数是:AfxEndDeferRegisterClass【WINCORE.cpp】
在注册函数里面可以看到各种各样的窗口;
在注册函数里是调用了AfxRegisterClass来注册的,这是个框架类的全局函数,在类里面可以直接调用;#Define AfxDeferRegisterClass AfxEndDeferRegisterClass
整个过程:设计(mfc设计好了),注册,产生,显示,更新,消息循环。
段点执行顺序:
1. CTestApp theApp 全局对象 来启动这个应用程序,this指针指向这个全局对象。如果不用这个全局对象。编译不会出错,但是运行的时候会出错。
2. CTestApp::CTestApp()析构函数 CWinApp完成了初始化工作。同时把子类的指针保存起来。
3.WinMain函数, 会跳AFxWinMain函数,
在AFxWinMain函数里会执行子类指针,调用InitInstance()【是个虚函数】会调用子类的InitInstance() 完成窗口类的注册,创建,显示,更新,等等。
4.会调用 AfxEndDeferRegisterClass
5.CMainFranme ::PreCreateWindow 再次调用AfxDeferRegisterClass去注册我们的窗口类。
6. AfxEndDeferRegisterClass。
7.CFrameWnd::Create 调用createEx 是继承而来的函数
8.CFrameWnd::PreCreateWindow 【PreCreateWindow是个虚函数,会调用子类的PreCreateWindow】
在函数中有ex的,表示扩展函数。
创建窗口:
Create函数【WINFRM.cpp】
调用CreateEx函数;【WINCORE.cpp中】
显示窗口:
在CTestApp类成员函数InitInstance中;
代码实例:
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
数据成员m_pMainWnd是一个指针,保存了应用程序框架窗口的一个指针;
第3课MFC程序框架之四消息循环【参考资料:孙鑫VC++教学视频】
我们来研究MFC程序里面的消息循环:
CWinThread::Run函数里完成消息循环;
里面有CWinThread::PumpMessage() 消息循环
里面有 if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
CMainFrame 和CTestView都是窗口类。
CTestApp是应用程序类。有且只有一个应用程序类。
CTestDoc类 继承于CDocument类 :是文档类。数据的存储加载。
数据的处理显示都是由CTestView类来实现。
CAboutDlg 是可有可无的,是专门帮助之类的功能。是从CWnd继承下来的。
第3课MFC程序框架之五补充【参考资料:孙鑫VC++教学视频】
当我们在查阅msdn(vc的帮助时),会出现很多个结果,其中一个就是:
Windows User Interface:Platform SDK;
这个其实就是win32 API函数的意思;
模拟CWnd
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; //因为是BOOL类
else
return FALSE;
}
BOOL CWnd::ShowWindow(int nCmdShow)
{
return ::ShowWindow(m_hWnd,nCmdShow);
//为了区分,平台函数,和类函数一样的时候。所以加两个冒号。
//表示所使用的,是全局函数。::之前不要加任何东西。
}
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; //如int a;定义
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
......
RegisterClass(&wndcls); //注册
CWnd wnd; 类,wnd不是窗口,是对象,如果关闭窗口,但是不会销毁对象。要看C++对象的生命周期。
wnd.CreateEx(...);
wnd.ShowWindow(SW_SHOWNORMAL);
wnd.UpdateWindow();
//…..和上面的比较。。以前是如下写的,现在要像上面那样写。
HWND hwnd;
hwnd=CreateWindowEx();
::ShowWindow(hwnd,SW_SHOWNORMAL);
::UpdateWindow(hwnd);
......
}
如果平台SDK函数为了和类的成员函数区分(主要是两个函数名字是一样的),我们是这样表示的:
::ShowWindow(…)
这样表示这个ShowWindow是平台SDK函数,Win32API函数,是一个全局函数;
还有return ::UpdateWindow(m_hWnd); 也是同样的道理;
★ CWnd wnd;类对象是窗口吗?不是
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);
......
}
蓝色的语句中CWnd wnd表示一个类对象;红色的语句中HWND hwnd表示一个对话框的句柄;
如果是同一个东西的话,他们的消亡应该是同时的,但是实际上并不是同时的。类对象的释放后,窗口句柄依然可以存在,除非人为同时释放;
所以CWnd类对象和窗口不是同一个东西;
第3课MFC程序框架之六创建按钮【参考资料:孙鑫VC++教学视频】
直接实践:
第一步:框架函数创建后,才能创建button函数。
在CMainFrame类里面,增加一个数据成员,CButton m_btn; 私有的;在最前面。
第二步:wm=window message
在WM_CREATE这个消息响应函数里面,创建按钮:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//位置在这个响应函数的最后
m_btn.Create("维新",WS_CHILD | BS_DEFPUSHBUTTON, CRect (0,0,100,100),
this,123); //123是编号。 //设定this 即 CmainFrame为父窗口!
m_btn.ShowWindow(SW_SHOWNORMAL);
return 0;
}
m_**** 变量名,表示类的成员变量。
如果我们修改m_btn.Create函数:
m_btn.Create("维新",WS_CHILD|WS_VISIBLE | BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123);
则m_btn.ShowWindow(…); 可以不要;WS_VISIBLE 是可视化的意思
按钮出现在客户区域,在菜单的下面;此时是在CMainFrame里的。
下面我们用同样的方法,在view类里面做同样的事情,没有OnCreate函数,但是OnCreate函数要自己增加,使用增加消息处理器message handlers;
同样的步骤。。。。。。
此时的按钮是在view里显示。
接下去,要讨论一下框架类的指针如何获得(也就是不用this),直接使用框架类的指针?
框架类是View类的一个父窗口;
用CWnd::GetParent()函数来实现;
m_btn.Create("维新",WS_CHILD | WS_VISIBLE | BS_AUTO3STATE,CRect(0,0,100,100),GetParent(),123);
★按钮的位置和代码所放的位置无关,和父窗口的指针有关;