我新建了一个MFC app工程,名字叫mfcTest,然后VC6++给我自动生成了一些类。CAboutDlg类,CMainFrame类,CMfcTestApp类,CMfcTestDoc类,CMfcTestView类.
接下来,我要一个个学习这种类。这些类都有基类,这些基类都是MFC的类库里面的。
CMfcTestApp 是继承了CWinApp类。
The CWinApp class is the base class from which you derive a Windows application object. An application object provides member functions for initializing your application (and each instance of it) and for running the application.
CMfcTestApp theAPP;在自动生成的代码中,CMfcTestApp声明了一个全局对象theAPP。也就是,我们打开的窗口什么的,都是theAPP这个对象。
Afx:Application Framework应用程序框架,集成了很多类用来设置。
CMainFrame类生成的是框架窗口,就如下图的蓝色线,也就是app最大的那个窗口框。CMfcTestView产生的就是那个子窗口。就是用粗线描绘的地方。
CMfcDoc类是用来加载一些数据,然后CMfcView类是用来显示这些数据的。
CAboutDlg:关于对话框的类,是继承Cwnd类,也是一种窗口类。
在第三课中,自动生成的代码,在内部是怎么进行的呢?
step 1.CMfcTestApp类声明一个全局对象theAPP this 指针指向这个对象
step 2.进入winmain()函数,在MFC\SRC\APPMODUL.CPP 中。他是这么定义的。
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
最后跳转到AfxWinMain();函数当中去了。
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// 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();
goto InitFailure;
}
nReturnCode = pThread->Run();
在这个函数中,我们进行了一些初始化的工作。
然后进入一个pThread->InitInstance()函数,在这个函数中,完成应用程序的初始化工作,注册,创建,显示,更新窗口类。
step 3.注册一个窗口类 我们用了AfxEndDeferRegisterClass这个函数。这个函数在...\MFC\SRC\WINCORE.CPP 中。AfxRegisterClass这个也是注册这个窗口
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
// mask off all classes that are already registered
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
fToRegister &= ~pModuleState->m_fRegisteredClasses;
if (fToRegister == 0)
return TRUE;
LONG fRegisteredClasses = 0;
// common initialization
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults
wndcls.lpfnWndProc = DefWindowProc;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hCursor = afxData.hcurArrow;
INITCOMMONCONTROLSEX init;
init.dwSize = sizeof(init);
// work to register classes as specified by fToRegister, populate fRegisteredClasses as we go
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);
}
.....
// must have registered at least as mamy classes as requested
return (fToRegister & fRegisteredClasses) == fToRegister;
}
step 4:创建窗口 这里用的是CreateEX.代码如下,在WINCORE.CPP中可以看见。
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID,
LPVOID lpParam /* = NULL */)
{
return CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(), (HMENU)nID, lpParam);
}
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
// allow modification of several common create parameters
CREATESTRUCT cs;//这个CREATESTRUCT 就是为了能够让程序员在创建窗口之前,修改一些基本的窗口参数
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hWndParent;
cs.hMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle();
cs.lpCreateParams = lpParam;
if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this);
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
step 5: 显示 和 更新 窗口
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
step 6:消息循环 在 ...\MFC\SRC\THRDCORE.CPP
int CWinThread::Run()
{
ASSERT_VALID(this);
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}
// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();
// reset "no idle" state after pumping "normal" message
if (IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
知识点补充:我们自己在写一些类的成员函数的时候,比如我们定义了一个CWnd::ShowWindow();但是我们在实现的时候用了SDK的API函数。
就可以这样 BOOL CWnd::ShowWindow(int nCndShow){ ::ShowWindow()} // ::ShowWindow()这样就是表示调用了SDK的API函数。
BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
const RECT& rect 这是一个结构体的引用。结构体本质上也是一种class,于是我们用CRect类的构造函数,声明了一个对象来代替这个结构体。比如,这个参数
就可以设置成为 CRect(0,0,100,100)
int CMainFrame::OnCreate();函数专门用来处理窗口创建时的消息int CMfcTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_btn.Create("weixin",WS_CHILD|WS_VISIBLE|BS_AUTO3STATE ,CRect(0,0,100,100),/*GetParent()*/this,123);
m_btn.ShowWindow(SW_SHOWNORMAL); // TODO: Add your specialized creation code here
return 0;
}
上面的GetParent()是为了得到父窗口的指针,this是theAPP的指针,