-《深入浅出MFC》学习笔C?nbsp;
作者:XGM 2001-8-14
MFCE序也是WindowsE序Q所以它也有一个WinMainQ但是我们在E序中看不到它的t媄。实际上Q在E序q入点之前,有一个(而且只有一个)全局对象Q在HelloE序中名为theAppQ,q是所谓的application
objectQ当操作pȝ程序加载ƈȀzLQ这个全局对象获得配置Q其构造函C先执行,比WinMain更早?
书中所举HelloE序Q是一个简单的MFCE序Q其M在于WinMain和WndProcQ而这两个部分其实都有相当E度的不变性。MFC是把有着相当固定行ؓ的WinMain内部操作装在CWinApp中;把有着相当固定行ؓ的WndProc内部操作装在CFrameWnd中。也是_CWinApp代表E序本体QCFrameWnd代表一个主框窗?
虽然WinMain和WndProc内部操作有相当程度的不变性,但面对不同应用程序也需有变化,所以必Mq两个类为基Q派生自qc,q改写其中一部分成员函数?
CWinApp----取代WinMain的地?
传统上SDKE序的WinMain所完成的工作现由CWinApp的三个函数完成:
virtual BOOL InitApplication( );
virtual BOOL InitInstance( );
virtual BOOL Run( );
CFrameWnd-----取代WndProc的地?
CFrameWnd主要用来掌握一个窗?BR> 引爆?-----Application object
当执行HelloE序Ӟq个全局对象产生Q于是构造函敎ͼ见APPCORE.CPPQ执行v来,CWinApp之中的成员变量将因ؓq个全局对象的诞生而获得配|与初|配置完成后,WinMainQMFC早已准备好,q由链接器直接加到应用程序中ȝQ登场?
AfxWinInitQ是lCWinApp构造函C后的W一个操作;
此后的操作是pApp->InitApplication(其中的pApp指向CMyWinApp对象Q即本例中的theApp)Q因为CMyWinAppl承自CWinAppQ而InitApplication又是CWinApp的一个虚拟函敎ͼ我们没有改写它(大部分情况下也不需要改它)Q所以上q操作相当于调用CWinAppQ:InitApplication。此E序的代码出现在APPCORE.CPP中;
lInitApplication之后QAfxWinMain调用pApp->InitInstanceQInitInstance是CWinApp的一个虚拟函敎ͼ应用E序一定要改写q个函数Q因为它在CWinApp中是个空函数Q没有Q何默L作)Q我们改写了它,所以上q操作就是调有我们自qq个InitInstance函数Q我们将在该处展开我们的主H口生命?
CMyWinAppQ:InitInstance一开始new了一个CMyFrameWnd对象Qnew会引发构造函数CmyFrameWndQ:CMyFrameWndQ其中调用了CFrameWnd的成员函数CreateQ它生一个窗口?
Create函数共八个参敎ͼW一个,指定WNDCLASSH口c,如果攄NULLQ表C以MFC内徏的窗口类产生一个标准的外框H口QCreate函数在生窗口之前会引发H口cȝ注册操作Q下一D讲q这一内容Q;W二个,指定H口标题Q第三个Q指定窗口风|默认是WS-OVERLAPPEDWINDOWQ如果你不想要窗口右上角的极大极钮Q可以改成WS-OVERLAPPED|WS-CAPTION|WS-SYSMENU|WS-THICKFRAME|WS-MINIMIZEBOX|WS-MAXIMIZEBOXQ如果希望有垂直滚动条,再加上WS-VSCROLLQ第四个参数Q指定窗口的位置与大,默认值rectDefault
Q第五个Q指定父H口Q第六个指定菜单Q第七个Qؓ扩充风格Q唯有以QCreateWindowEx(而非QCreateWindow)函数才能完成Q事实上QCFrameWndQCreate最l调用的正是QCreateWindowExQ第八个Q是一个指向CCreateContextl构的指针,framework利用它,在具备Document/Viewl构的程序中初始化外框窗口,默认值NULL
CFrameWndQCreate在函C调用CreateExQCWnd有这个成员函敎ͼ但其zcCFrameWndq没有,所以这里调用的实际上是CWndQCreateExQ;后者又调用PreCreateWindow虚拟函数Q它在CWnd及其zcCFrameWnd都有定义Q所以实际上调用的是CFrameWndQ:PreCreateWindowQ,q个函数调用了AfxDeferRegisterClass宏,它表C如果变量afxRegisteredClass的值显C系l已l注册了fClass
q种H口c,MFC啥也不做Q否则就调用AfxEndDeferRegisterClass(fClass){它调用两个函数完成实际的H口cL册操作,一个是RegisterWithIconQ一个是AfxRegisterClass}Q准备注册之?
H口昄与更?BR> CMyFrameWndQ:CMyFrameWndl束后,H口已经诞生出来Q程序又回到CMyWinAppQ:InitInstanceQ于是调用ShowWindow函数令窗口显C出来,q调用UpdateWindow函数令HelloE序送出WM-PAINT
CWinAppQ:Run----E序生命的活水源?
Run又是CWinApp的一个虚拟函敎ͼ我们没有改写它(大部分情况下也不需要改它)Q所以上q操作相当于调用CWinAppQ:Run
WinMain已由MFC提供Q窗口类已由MFC注册完成Q连H口函数也都由MFC提供
把消息与处理函数q接在一PMessage Map机制
MFC提供l应用程序用的“很方便的接口”是两组宏,以HelloZQ第一个操作是在Hello.h的CMyFrameWnd加上DECLARE-MESSAGE-MAPQ第二个操作是在Hello.cpp的Q何位|(当然不能在函数内Q用宏
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_COMMAND(IDM_ABOUT, OnAbout)
ON_WM_PAINT()
END_MESSAGE_MAP()
来龙去脉L?
E序的诞?
Application object 产生Q?内存于是获得配置Q初g讄了?
AfxWinMain执行AfxWinInitQ后者又调有AfxInitThreadQ把消息队列量?
大到96
AfxWinMain执行InitApplication。这是CWinApp的虚拟函敎ͼ我们通常不改写它
AfxWinMain执行InitInstance。这是CWinApp的虚拟函敎ͼ我们必须改写?
CMyWinAppQ:InitInstance “new”了一个CMyFrameWnd对象
CmyFrameWnd构造函数调用CreateQ生主H口。我们在Create参数中指定的H口cLNULLQ于是MFCҎH口U类Q自行ؓ我们注册一个名为“AfxFrameOrView42d”的H口cR?
回到InitInstance中l执行ShowWindowQ显C窗?
执行UpdateWindowQ于是发出WM-PAINT
回到AfxWinMainQ执行RunQ进入消息@环?
E序开始运行:
E序获得WM-PAINT消息Q由CWinAppQ:Run中的Q:GetMessage循环Q?
WM-PAINTl由Q:DispatchMessage送到H口函数CWnd::DefWindowProc中?
CWnd::DefWindowProc消息传递到消息映射表格
传递过E中发现有相W项目,于是调用目中对应的函数。此函数是利用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏讄h的?
标准消息的处理程序亦有标准命名,例如WM-PAINT必由OnPaint处理
E序的死亡:
使用者单击File/CloseQ于是发出WM-CLOSE
CMyFrameWndq没有设|WM-CLOSE处理E序Q于是交l默认的处理E序
默认函数对于WM-CLOSE的处理方式是调用Q:DestroyWindowQƈ因而发出WM-DESTROY
默认的WM-DESTROY处理方式是调用:QPostQuitMessageQ因此发出WM-QUIT
CWinApp::Run收到WM-QUIT后会l束内部之消息@环,然后调用ExitInstanceQ这是CWinApp的一个虚拟函敎ͼ如果CMyWinApp改写了ExitInstanceQ那么CWinApp::Run所调用的就是CMyWinAppQ:ExitInstanceQ否则就是CWinAppQ:ExitInstance
最后回到AfxWinMainQ执行AfxWinTermQ结束程?
附HelloE序部分代码Q?
Hello.cpp
#include "Stdafx.h"
#include "Hello.h"
#include "Resource.h"
CMyWinApp theApp; // application object
//--------------------------------------------------------------------
// CMyWinApp's member
//--------------------------------------------------------------------
BOOL CMyWinApp::InitInstance()
{
m_pMainWnd = new CMyFrameWnd();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
//--------------------------------------------------------------------
// CMyWinApp's member
//--------------------------------------------------------------------
BOOL CMyWinApp::OnIdle(LONG lCount)
{
CMyFrameWnd* pWnd = (CMyFrameWnd*)m_pMainWnd;
pWnd->IdleTimeHandler(lCount);
return TRUE;
}
//--------------------------------------------------------------------
// CMyFrameWnd's member
//--------------------------------------------------------------------
CMyFrameWnd::CMyFrameWnd()
{
Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault,
NULL, "MainMenu"); }
//--------------------------------------------------------------------
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_COMMAND(IDM_ABOUT, OnAbout)
ON_WM_PAINT()
END_MESSAGE_MAP()
//--------------------------------------------------------------------
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++);
}
//--------------------------------------------------------------------
void CMyFrameWnd::OnAbout()
{
CDialog about("AboutBox", this); // "AboutBox"
about.DoModal();
}
//--------------------------------------------------------------------
void CMyFrameWnd::IdleTimeHandler(LONG lCount)
{
CString str;
CRect rect(10,10,200,30);
CDC* pDC = new CClientDC(this);
str.Format("%010d", lCount);
pDC->DrawText(str, &rect, DT_LEFT | DT_TOP);
}
Hello.h
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance(); //
virtual BOOL OnIdle(LONG lCount); // OnIdle e?^
};
//--------------------------------------------------------------------
class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd(); // constructor
afx_msg void OnPaint(); // for WM_PAINT
afx_msg void OnAbout(); // for WM_COMMAND (IDM_ABOUT)
void IdleTimeHandler(LONG lCount); // we want it call by
CMyWinApp::OnIdle
private:
DECLARE_MESSAGE_MAP() // Declare Message Map
static VOID CALLBACK LineDDACallback(int,int,LPARAM);
//?C
};
《深入浅出MFC》学习笔C一
作者:XGM RRC.BIT C2001-8-10
龚徏伟评Q这学习笔记虽然不是系l的阐述某个问题Q但L让h感到收获不小Q无Z是老鸟q是菜鸟Q都会认同的。我希望XGM能够l箋写下去,看了《深入浅出MFC》再来看q些W记Q也有温故知C?BR> 俟杰先生的《深入浅出MFC》是一本好书,本h是作为有一定C++基础知识q对VC有一Ҏ性认识的VC初学者来接触q本书的。应该说q本书ƈ不真正适合像我q样的初学者,但通读完该书,q是有一些收LQ现只整理其中的一部分Q作为我的一个学习笔记吧?BR> 1. 安装VC++Qؓ什么要先安装Internet
ExplorerQ(是不是很多老鸟也不知道啊,侯俊杰先生的宗旨是知其Ӟq要知其所以然Q?555555555 …………)
因ؓ微Y的所有Visual Tools(包括Visual C++、Visual Basic、Visual FoxPro、Visual
J++、Visual InterDev{?都集中由所谓的Visual Studio
理Q而这些工h一个极大的目标Q就是要协助开发Internet应用软gQ所以它们希望能够和IE有所搭配?BR> 2. SDKQSoftware Development Kit 原指软g开发工P但现在已l变成了一个专有名词,凡以Windows
raw API撰写的程序通常也称为SDKE序。也有h把Windows APIUCؓSDK API?BR> 3.
不同U类之对象的构造函敎ͼ对象诞生后第一个执行ƈ且是自动执行的函敎ͼ及析构函敎ͼ对象行将毁灭Q但未毁灭之前一刻,最后执行ƈ且自动执行的函数Q执行时机对比如下:
全局对象局部对象静态对象以new产生的对?BR> 构造函数程序一开始就执行Q比E序q入Ҏ早)对象诞生时执?BR> 析构函数E序卛_l束时执行程序流E离开该对象的存活范围时执行程序将l束时执行,但比全局对象的析构函数早一步进行。对象被delete时执?/P>
4. C++q不是纯U的面向对象语言QSmallTalk ?
Java才是Q。所以,MFC之中得以存在有不属于Mcȝ全局函数Q它们统l在函数名称开头冠以AfxQxq没有什么意思,只是Z凑成一个响亮的名字QAf即Application
FrameworkQ?BR> 下面是常见的Afx全局函数Q?BR> AfxBeginThreadQ开始一个新的线E?BR> AfxEndThreadQ结束一个旧的线E?BR> AfxFormatString1Q类似printf一般地字W串格式?BR> AfxFormatString2Q类似printf一般地字W串格式?BR> AfxMessageBoxQ类似Windows API 函数 MessageBox
AfxOuputDebugStringQ将字符串输往除错装置
AfxGetAppQ获得application object (CwinAppz对象)的指?BR> AfxGetMainWndQ获得程序主H口的指?BR> AfxGetInstanceQ获得程序的instance handle
5. MFC数据cd
下面q些是和Win32E序共同使用的数据类?BR> BOOLQ布|取gؓTRUE or FALSE
BSTRQ?2-bit 字符指针
BYTEQ?-bit整数Q未带正负号
COLORREFQ?2-bit数|代表一个颜色?BR> DWORDQ?2-bit整数Q未带正负号
LONGQ?2-bit整数Q带正负?BR> LPARAMQ?2-bit整数Q作为窗口函数或callback函数的一个参?BR> LPCSTRQ?2-bit指针Q指向一个常数字W串
LPSTRQ?2-bit指针Q指向一个字W串
LPCTSTRQ?2-bit指针Q指向一个常数字W串Q此字符串可以移植到Unicode和DBCS
LPTSTRQ?2-bit指针Q指向一个字W串Q此字符串可以移植到Unicode和DBCS
LPVOIDQ?2-bit指针Q指向一个未指定cd的数?BR> LPRESULTQ?2-bit数|作ؓH口函数或callback函数的返回?BR> UINTQ在Win16中是一?6-bit 未带正负h敎ͼ在Win32中是一?2-bit 未带 正负h敎ͼ
WNDPROCQ?2-bit指针Q指向一个窗口函?BR> WORDQ?6-bit 整数 Q未带正负号
WPARAMQ窗口函数或callback函数的一个参敎ͼ在Win16中是16-bitQ在Win32中是32-bit
下面q些是MFC独特的数据类?BR> POSITIONQ一个数|代表collection对象Q例如数l或链表Q中的元素位|,?用于MFC collection
classesQ即数据处理c,如CArrayQ?BR> LPCRECTQ?2-bit指针Q指向一个不变的RECTl构
6. 奇怪的H口cdUAfx:b:14ae:6:3e8f
用Spy++QVC++所附的一个工P观察H口cȝ名称Ӟ会发现窗口类名称变成像Afx:b:14ae:6:3e8fq副奇怪的模样Q?BR> 原来是Application Framework 把窗口类的名U{换ؓAfx:Q?x Qy Qz Qw的类型,成ؓ独一无二的窗口类型名Uͼ
xQ窗口风格的hex?BR> yQ窗口鼠标光标的hex?BR> zQ窗口后台颜色的hex?BR> wQ窗口图标的hex?BR> 7. Serializable 的必要条?BR> 让一个对象有Serializable能力Q它必须z自一个Serializablec,一个类要成为SerializableQ必L下列五大条gQ?BR> Q?Q从Cobjectz下来Q如此一来,可保有RTTI、动态生成等
Q?Q类的声明部分必LDECLARE_SERIAL宏,此宏需要一个参敎ͼcdU?BR> Q?Q类的实作部分必LIMPLEMENT_SERIAL宏,此宏需要三个参敎ͼ一是类名称Q二是基cdUͼ三是 schema no.
Q?Q改写Serialize虚拟函数Q它能够适当地把cȝ成员变量写入档案?BR> Q?Qؓ此类加上一个default构造函敎ͼ也就是无参数之构造函敎ͼQ这个条件常Zh所忽略Q但它是必要的,因ؓ若一个对象来自档案,MFC必须先动态生成它Q而且在没有Q何参数的情况下调用其构造函敎ͼ然后才从档案中读出对象数据?BR>
唉,好东东实在太多了Q比如对MFC六大关键技术之仿真Q三大宏的内部揭密等{(咦,我不是在推销q本书吧Q呵呵)感兴的话请看看q本书吧Q可以找我借哦Q所谓“书非借不能读也”。呵呵……?/P>