MFC学习笔记

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

MFC程序也有一个WinMain函数,这个函数是我们在编译链接的时候由链接器将WinMain函数链接进来的。

这个函数在APPMODUL.CPP中。

带有Afx的属于应用程序框架类的函数,应用程序框架是辅助我们生成应用程序的框架模型,这个框架模型把很多类或者类与类之间做了一个有机的集成提供给我们,我们可以根据框架模型提供的类库来设计我们自己的应用程序,Af即Application frame 看看这些类如何和我们WinMain函数关联到一起的?
CmainFrame的名字是不会变的,其它的都是C+工程名+App/Doc/View命名。

class CTestApp : public CwinApp,可以看到CtestApp是从CwinApp派生出来的。

当我们双击CtestApp这个类时,我们可以看到VC++6.0上面显示我们进入了Test.h头文件中,当把CtestApp展开时,然后双击任何一个函数名,包括构造函数名,析构函数名…我们可以看到VC++6.0上面显示我们进入了Test.cpp源文件中。

且这种情况和我们直接从Fileview 资源卡中直接进入相应的头文件和源文件是一样的效果。

其它类也是相似的操作进入头文件和源文件。

不管是全局变量还是在全局对象CTestApp theApp;,它们都是在程序运行之前,也就是在入口函数WinMain函数加载之前,就已经为它们分配好了内存空间,作为全局函数,就要调用构造函数创建内存空间。

所以是先运行全局对象CTestApp theApp,调用它的构造函数,然后才运行WinMain。

为什么要定义一个全局对象呢?为什么要让它在WinMain之前完成呢?全局对象theApp又有什么作用?CtestApp是从CwinApp派生出来的,theApp是应用程序对象,是一个全局对象,每一个MFC程序当中有且仅有一个从CwinApp派生出来的类,也只能有一个应用程序类实例化的对象,它就表示了我们应用程序本身。

全局的对象theApp会导致构造函数的调用,CtestApp是从CwinApp派生出来的,当一个子类的构造函数调用之前先要构造父类,所以导致了CwinApp构造函数的调用,CwinApp是谁写的呢?是微软提供给我们的,那么这个派生类就和微软提供给我们的基类就关联起来了,在CwinApp中对程序和初始化的工作在CwinApp的构造函数中就完成了。

CWinApp::CWinApp(LPCTSTR lpszAppName),CwinApp的构造函数带有一个参数,而继承它的CtestApp确没有参数,这是为什么呢?因为它在CwinApp进行函数原型声明时有一个默认值,CWinApp(LPCTSTR lpszAppName = NULL);
CWinApp::CWinApp(LPCTSTR lpszAppName)
{
if (lpszAppName != NULL)
m_pszAppName = _tcsdup(lpszAppName);
else
m_pszAppName = NULL;
// initialize CWinThread state
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
ASSERT(AfxGetThread() == NULL);
pThreadState->m_pCurrentWinThread = this;
因为this指针是指向对象本身,所以这个this指针是指向子类CtestApp的全局对象theApp。

当基类CwinApp实例化完成之后,子类CtestApp实例化完成之后,全局对象就有了内存空间,全局对象定义好了之后,接下来就该执行我们WinMain函数了。

在main函数中进
行设计窗口类、注册窗口类、创建窗口、显示窗口及更新窗口、消息循环机消息处理等工作。

extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
找到WinMain函数了,WinMain函数在APPMODUL.CPP中。

现在我们去找AfxWinMain 函数,AfxWinMain函数在WINMAIN.CPP中。

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
/*这个函数的的定义:CWinApp*AfxGetApp();
Return Value
A pointer to the single CWinApp object for the application. */
所以pApp是指向CWinApp object对象theApp。

// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations
if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
因为pApp指向CwndApp对象theApp,pThread指向CwinThread对象,而CwndApp 是从CwinThread派生出来的,所以pThread和pApp是指向的同一个对象theApp。

此时pApp 和pThread调用三个函数进行设计窗口类、注册窗口类、创建窗口、显示窗口及更新窗口、消息循环机消息处理等工作。

pThread->InitInstance()时,这个时候CWinThread、CwinApp、CtestApp都有InitInstance()这个函数,但是在CwinThread和CwinApp中InitInstance()是虚函数,根据C++多态性原理,当基类函数为虚函数时,应调用子类的,所以pThread->InitInstance()是调用的子类CtestApp的InitInstance()函数。

在MFC中,当注册窗口类时,注册窗口函数AfxEndDeferRegisterClass根据窗口的类型自动设计窗口类,AfxEndDeferRegisterClass函数在WINCORE.CPP中。

也就是注册窗口函数AfxEndDeferRegisterClass会根据不同的窗口类去进行注册。

这里它进行了一个判断:if (fToRegister & AFX_WND_REG)
{
// Child windows - no brush, no icon, safest default class styles
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpszClassName = _afxWnd;
if (AfxRegisterClass(&wndcls))
fRegisteredClasses |= AFX_WND_REG;
}
if (fToRegister & AFX_WNDOLECONTROL_REG)
{
// OLE Control windows - use parent DC for speed
wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpszClassName = _afxWndOleControl;
if (AfxRegisterClass(&wndcls))
fRegisteredClasses |= AFX_WNDOLECONTROL_REG;
}
if (fToRegister & AFX_WNDCONTROLBAR_REG)
{
// Control bar windows
wndcls.style = 0; // control bars don't handle double click
wndcls.lpszClassName = _afxWndControlBar;
wndcls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
if (AfxRegisterClass(&wndcls))
fRegisteredClasses |= AFX_WNDCONTROLBAR_REG;
}
if (fToRegister & AFX_WNDMDIFRAME_REG)
{
// MDI Frame window (also used for splitter window)
wndcls.style = CS_DBLCLKS;
wndcls.hbrBackground = NULL;
if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame, AFX_IDI_STD_MDIFRAME))
fRegisteredClasses |= AFX_WNDMDIFRAME_REG;
}
if (fToRegister & AFX_WNDFRAMEORVIEW_REG)
{
// SDI Frame or MDI Child windows or views - normal colors
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME))
fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;
}
if (fToRegister & AFX_WNDCOMMCTLS_REG)
{
// this flag is compatible with the old InitCommonControls() API
init.dwICC = ICC_WIN95_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WIN95CTLS_MASK);
fToRegister &= ~AFX_WIN95CTLS_MASK;
}
if (fToRegister & AFX_WNDCOMMCTL_UPDOWN_REG)
{
init.dwICC = ICC_UPDOWN_CLASS;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_UPDOWN_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_TREEVIEW_REG)
{
init.dwICC = ICC_TREEVIEW_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TREEVIEW_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_TAB_REG)
{
init.dwICC = ICC_TAB_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TAB_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_PROGRESS_REG)
{
init.dwICC = ICC_PROGRESS_CLASS;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_PROGRESS_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_LISTVIEW_REG)
{
最后还判断注册是否成功,这里也进行了判断:
BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)
{
WNDCLASS wndcls;
if (GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,
&wndcls))
{
// class already registered
return TRUE;
}
if (!::RegisterClass(lpWndClass))
{
TRACE1("Can't register window class named %s\n",
lpWndClass->lpszClassName);
return FALSE;
}
现在设计窗口类和注册窗口类之后就要进行创建窗口了,创建窗口的时候需要给窗口一个名字。

从CWnd派生出来的类都是一个窗口类。

CframeWnd类是从CWnd类派生出来的,CmainFrame是从CframeWnd派生出来的,它是一个框架窗口,也就是整个应用程序窗口。

Cview也是从CWnd派生出来的,CtestView是从Cview派生出来的,所以它也是一个窗口类,只是一个子窗口。

在MFC中,CDocument类的作用,CtestDoc类的作用:将数据本身和它的显示分离开来就由文档类CTestDoc类来完成,数据的显示和修改就由视图CtestView类完成。

那么,MFC内部是怎么将CmainFrame、CtestView、CtestDoc三者联系到一起的呢?在CtestApp中的InitInstance()函数
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
这段话的意思是在InitInstance()函数中定义了一个pDocTemplate指针,然后将CmainFrame、CtestView、CtestDoc三者联系到一起。

然后添加到(AddDocTemplate)单文档模板中。

当窗口销毁的时候,C++的对象wnd还没有被销毁。

C++对象的生命周期和窗口的生命周期是不一样的。

C++对象销毁的时候,窗口一般应该也销毁。

但窗口销毁的时候对象还没销毁。

唯一的联系是C++内部定义了
一个C++窗口的句柄。

所以创建一个窗口对象btn时不能显示窗口的,还需要用create函数创建窗口,如下:
因为CButton也是从CWnd函数派生来的,所以也是窗口类,所以CButton不仅具有窗口的特点,还具有按钮的特点。

// m_btn.Create("陈东群",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123);
/*
为什么没有按钮产生呢?因为CButton是一个局部变量,当执行到这个程序的花括号时,这个局部变量生命周期就结束了,发生了析构,
这时按钮也就跟着没了.
定义成全局变量之后还是没有显示按钮?为什么呢?因为创建一个窗口之后还需要显示,
*/
// m_btn.ShowWindow(SW_SHOWNORMAL);
//如果想要在子窗口里面获得父窗口的指针,将按钮显示在父窗口的位置怎么办?用GetParent函数就可以了
//所以说按钮的位置并不是我们把代码写在哪个窗口,按钮就跟着显示在哪个窗口
//这里的ShowWindow函数也没有用窗口句柄,因为CButton是从CWnd派生出来的,所以//也把句柄封装到了类当中,作为该类的成员变量,来保存了和类对象相关的窗口句柄。

//当我们要去完成某个功能时,可以凭一种感觉,利用单词的组合去拼写出一个函数来。

相关文档
最新文档