22、深入浅出MFC学习笔记,MFC解剖相关
1、需要的函数库
Windows C Runtime函数库
DLL Import函数库
MFC 函数库[1,P264]
2、传统SDK程序中WinMain所完成的工作现在由CWinApp的三个函数完成:
virtual BOOL InitApplication();//内部管理初始化工作
virtual BOOL InitInstance();//创建窗口
virtual int Run();//分发消息
WinMain只是扮演驾驭它们的角色。
CFrameWnd取代WndProc的地位。
一般CMyWinApp要改写CWinApp中的InitInstance,而不改写InitApplication和Run。
3、在创建窗口时,改变图标和鼠标光标形状的介绍,可以参见[1,P289]。
4、奇怪的窗口类名称 Afx:b:14ae:6:3e8f[1,P290]
用 spy++ 这类工具观察窗口类名称, 却发现窗口类名称变成这幅奇怪模样, 原来是 Application Framework 玩了一些把戏, 它把这些窗口类名称转换为 Afx:x:y:z:w 的形式, 成为独一无二的窗口类的名称:
x: 窗口风格(window style) 的 hex 值
y: 窗口鼠标光标的 hex 值
z: 窗口后台颜色的 hex 值
w: 窗口图标(icon)的hex 值
5、MFC来龙去脉整理
程序的诞生
Application object 产生, 内存于是获得配置,初值亦设立了。
AfxWinMain执行AfxWinInit,后者又调有AfxInitThread,把消息队列尽量加
大到96
AfxWinMain执行InitApplication。这是CWinApp的虚拟函数,我们通常不改写它
AfxWinMain执行InitInstance。这是CWinApp的虚拟函数,我们必须改写它
CMyWinApp:: InitInstance “new”了一个CMyFrameWnd对象
CmyFrameWnd构造函数调用Create,产生主窗口。我们在Create参数中指定的窗口类是NULL,于是MFC根据窗口种类,自行为我们注册一个名为“AfxFrameOrView42d”的窗口类。
回到InitInstance中继续执行ShowWindow,显示窗口
执行UpdateWindow,于是发出WM-PAINT
回到AfxWinMain,执行Run,进入消息循环。
程序开始运行:
程序获得WM-PAINT消息(由CWinApp::Run中的::GetMessage循环)
WM-PAINT经由::DispatchMessage送到窗口函数CWnd::DefWindowProc中。
CWnd::DefWindowProc将消息传递到消息映射表格
传递过程中发现有相符项目,于是调用项目中对应的函数。此函数是利用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏设立起来的。
标准消息的处理程序亦有标准命名,例如WM-PAINT必由OnPaint处理
程序的死亡:
使用者单击File/Close,于是发出WM-CLOSE
CMyFrameWnd并没有设置WM-CLOSE处理程序,于是交给默认的处理程序
默认函数对于WM-CLOSE的处理方式是调用::DestroyWindow,并因而发出WM-DESTROY
默认的WM-DESTROY处理方式是调用::PostQuitMessage,因此发出WM-QUIT
CWinApp::Run收到WM-QUIT后会结束内部之消息循环,然后调用ExitInstance,这是CWinApp的一个虚拟函数;如果 CMyWinApp改写了ExitInstance,那么CWinApp::Run所调用的就是CMyWinApp::ExitInstance,否则就是CWinApp::ExitInstance
最后回到AfxWinMain,执行AfxWinTerm,结束程序
6、Callback函数
凡是由程序员设计而却由Windows 系统调用的函数,统称为callback 函数。这些函数都有一定的类型,以配合Windows的调用动作。
要把某个函数用作callback 函数,就必须告诉C++ 编译器,不要放this 指针作为该函数的最后一个参数[5]。
两个方法可以做到这一点:
1)不要使用类的成员函数(也就是说,要使用全域函数)做为callback 函数。
2)使用static 成员函数。也就是在函数前面加上static 修饰词。
为什么要这样呢?
因为callback 函数是给Windows调用用的,Windows 并不经由任何对象调用这个函数,也就无法传递this 指针给callback 函数,于是导致堆栈中有一个随机变量会成为this 指针,而其结果当然是程序的崩溃了。
内存中只会有一份类成员函数,但却可能有许多份类成员变量(每个对象拥有一份)。
示例 LineDDA的使用
The LineDDA function determines which pixels should be highlighted for a line
defined by the specified starting and ending points. 这个函数以Bresenham算法计算出通过两点之间直线中的每一个屏幕像素坐标;每计算出一个坐标,就通知由LindDDA第五个参数所指定的callback函数。
示例代码
class CMyFrameWnd : public CFrameWnd { .... private: DECLARE_MESSAGE_MAP() // Declare Message Map static VOID CALLBACK LineDDACallback(int,int,LPARAM); };
.cpp
void CMyFrameWnd::OnPaint() { CPaintDC dc(this); CRect rect; GetClientRect(rect); dc.SetTextAlign(TA_BOTTOM | TA_CENTER); ::LineDDA(rect.right/2, 0, rect.right/2, rect.bottom/2, (LINEDDAPROC) LineDDACallback, (LPARAM) (LPVOID) &dc); } //-------------------------------------------------------------------- VOID CALLBACK CMyFrameWnd::LineDDACallback(int x, int y, LPARAM lpdc) { static char szText[] = "Hello, MFC"; ((CDC*)lpdc)->TextOut(x, y, szText, sizeof(szText)-1); for(int i=1; i<50000; i++); //延迟 }
空闲时间的处理,通用对话框[1,P300]
参考
[1] 深入浅出MFC
[2] MFC Technical Notes
http://msdn.microsoft.com/en-us/library/h6h0eact%28VS.80%29.aspx
[3] http://blog.163.com/zhoumhan_0351/blog/static/3995422720103401415721/
[4] http://blog.163.com/zhoumhan_0351/blog/static/3995422720103411551637/
[5] http://www.cnblogs.com/mydomain/archive/2011/02/25/1965310.html