生死MFC程序

类别:VC语言 点击:0 评论:0 推荐:

MFC将大部分函数都进行了封装,程序员只要改写部分重要的virtual函数即可,这往往使初学者摸不着头脑,连个WinMain函数都看不到,程序从哪开始从哪结束?基本的条理搞不清,永远也不会有提高。下面简单讲下基运行过程.
1,CMyWinApp theApp  程序从这里开始
2,_tWinMain()   在APPMODUL.CPP 它实际上只调用AfxWinMain函数
3,AfxWinMain()   WINAMIN.CPP,去掉一些次要信息,它作的事就是:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpCmdLine, int nCmdShow)
{
 int nReturnCode = -1;
 CWinThread* pThread = AfxGetThread();

 CWinApp* pApp = AfxGetApp();  ->实际上就是取得CMyWinApp对象指针
 AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)

 pApp->InitApplication()
 pThread->InitInstance()
 pThread->Run();


 AfxWinTerm();
 return nReturnCode;
}

其中:
AfxGetApp();   AFXWIN.CPP中
InitApplication()  AFXWIN.CPP中
InitInstance()   AFXWIN.CPP中

实际上AfxWinMainy就是调用:
 CWinApp::InitApplication 因为我们程序没有改写它
 CMyWinApp::InitInstance  我们改写了它且必须改写它,为什么?看源码就能证明一切

BOOL CWinApp::InitInstance() APPCORE.CPP中
{
 return TRUE;
}
看到了吧,它什么也没干,直接return TRUE;

 CWinApp::Run();

4:AfxWinInit()   AFX内部初始化操作,APPINIT.CPP中,贴下源码,如下:
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpCmdLine, int nCmdShow)
{
 ..,去掉部分
 pModuleState->m_hCurrentInstanceHandle = hInstance;
 pModuleState->m_hCurrentResourceHandle = hInstance;

 CWinApp* pApp = AfxGetApp();
 if (pApp != NULL)
 {
  // Windows specific initialization (not done if no CWinApp)
  pApp->m_hInstance = hInstance;
  pApp->m_hPrevInstance = hPrevInstance;
  pApp->m_lpCmdLine = lpCmdLine;
  pApp->m_nCmdShow = nCmdShow;
  pApp->SetCurrentHandles();
 }
 if (!afxContextIsDLL)
  AfxInitThread();

 return TRUE;
}
可以看到。它主要进行一些初始化工作.其中:
 AfxInitThread()  在THRDCORE.CPP中

5:CWinApp::InitApplication() APPCORE.CPP中,
BOOL CWinApp::InitApplication()
{
 if (CDocManager::pStaticDocManager != NULL)
 {
  if (m_pDocManager == NULL)
   m_pDocManager = CDocManager::pStaticDocManager;
  CDocManager::pStaticDocManager = NULL;
 }
 if (m_pDocManager != NULL)
  m_pDocManager->AddDocTemplate(NULL);
 else
  CDocManager::bStaticInit = FALSE;
 return TRUE;
}
5:CMyWinApp::InitInstance() 继InitApplication后就是InitInstance调用了。在我们改写的InitStance函数中,将是我们的主窗口的生命,在这里我们会有这样的操作:
m_pMainWnd=new CMyFrameWnd();开始我们的主窗口,创建主窗口,MDI程序采取的是LoadFrame,大家可以看CFrameWnd::LoadFrame()(WNDFRM.CPP中),实际还是做了一样的事,大家不防看下源码。

6:CFrameWnd::Create()

BOOL CFrameWnd::Create(LPCTSTR lpszClassName, 类名
 LPCTSTR lpszWindowName,   窗口名
 DWORD dwStyle,    样式
 const RECT& rect,   区域,默认:rectDefault
 CWnd* pParentWnd,   父窗口
 LPCTSTR lpszMenuName,   菜单
 DWORD dwExStyle,   扩展样式
 CCreateContext* pContext)
{
 HMENU hMenu = NULL;
 if (lpszMenuName != NULL)
 {
  HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);
  if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
  {
   TRACE0("Warning: failed to load menu for CFrameWnd.\n");
   PostNcDestroy();            // perhaps delete the C++ object
   return FALSE;
  }
 }
 m_strTitle = lpszWindowName;    // save title for later
 if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
  rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
  pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
 {
  TRACE0("Warning: failed to create CFrameWnd.\n");
  if (hMenu != NULL)
   DestroyMenu(hMenu);
  return FALSE;
 }

 return TRUE;
}

7:CWnd::CreateEx()  可以看到CFrameWnd::Create,只是调用了CreateEx 函数,CFrameWnd没有改字CreateEx 函数,所以它调用的是CWnd 类的函数.

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;   用过SDK写程序的朋友一定知道这是想做什么
 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);
...
}
此函数又调用了PreCreateWindow,CreateWindowEx,

8:CFrameWnd::PreCreateWindow(), PreCreateWindow是个virtual,由于this 指针的原故,这里是调用CFrameWnd的PreCreateWindow.一般我们的程序都会改写些函数,在此设定窗口的一些样式。

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)  --->WINFRM.CPP
{
 if (cs.lpszClass == NULL)
 {
  VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
  cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background
 }
...
}

#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)

AfxEndDeferRegisterClass()  --->WINCORE.CPP,在这个函数中,调用RegisterWithIcon(实际上还是要调用AfxRegisterClass),和AfxRegisterClass,会我们注五个窗口类.在non-Unicode下使用MFC动态链接版和谳试版,五个类为:
"AfxWnd42d"
"AfxControlBar42d"
"AfxMDIFrame42d"
"AfxFrameOrView42d"
"AfxOleControl42d"
在Uncode中使用静态和调试版,是:
"AfxWnd42sud"
"AfxControlBar42sud"
"AfxMDIFrame42sud"
"AfxFrameOrView42sud"
"AfxOleControl42sud"

大家可以看到了。PreCreateWindow是在窗口产生前被调用,用来注册窗口类,如果我们指定的窗口类为NULL,则会用系统默认的。可以来看下不同功能的窗口使用的窗口类:
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs) -->WINCORE.CPP
{
 if (cs.lpszClass == NULL)
 {
  ...
  cs.lpszClass = _afxWnd;  file://使用_afxWnd窗口类
 }
}

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) -->WINFRM.CPP
{
 if (cs.lpszClass == NULL)
 {
  ...
  cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background
 }
}

BOOL CMDIFrameWnd::PreCreateWindow(CREATESTRUCT& cs) -->WINMDI.CPP
{
 if (cs.lpszClass == NULL)
 {
  ...
  cs.lpszClass = _afxWndMDIFrame;  file://使用_afxWndMDIFrame 窗口类
 }
 return TRUE;
}

...

9:CWinApp::Run()  程序到这里,窗口类注册好了。并显示出来了。UpDateWindow函数将会调用,并产生一个WM_PAINTIITH,等待处理。便进入了RUN函数,
file://APPCORE.CPP
int CWinApp::Run()
{
 if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
 {
  // Not launched /Embedding or /Automation, but has no main window!
  TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");
  AfxPostQuitMessage(0);
 }
 return CWinThread::Run();  // 调用CWinThread::Run()
}

file://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
}

如果没有消息,则调用OnIdle(),空闲处理,否则PumpMessage(),看下它做了什么?
BOOL CWinThread::PumpMessage()
{
 ASSERT_VALID(this);

 if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
 {
#ifdef _DEBUG
  if (afxTraceFlags & traceAppMsg)
   TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");
  m_nDisablePumpCount++; // application must die
   // Note: prevents calling message loop things in 'ExitInstance'
   // will never be decremented
#endif
  return FALSE;
 }

#ifdef _DEBUG
 if (m_nDisablePumpCount != 0)
 {
  TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n");
  ASSERT(FALSE);
 }
#endif

#ifdef _DEBUG
 if (afxTraceFlags & traceAppMsg)
  _AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
#endif

 // process this message

 if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
 {
  ::TranslateMessage(&m_msgCur);
  ::DispatchMessage(&m_msgCur);
 }
 return TRUE;
}

呵,和SDK程序做法一样,GetMessage,TranslateMessage(),DisptchMessage()
好了。进入到Run ()这里,程序就活动起来了。它会对一系列的消息进行处理。并在收到WM_CLOSE 消息时退出.默认函数对WM_CLOSE的处理是是调用::DestroyWindow(),关因而发出WM_DESTROY消息,默认的的WM_DESTROY处理方式是调用::PostQuitMessage(),因此发出WM_QUIT,Run收到WM_QUIT时,就结束消息循环,调用ExitInstance,最后在AfxWinMain中执行AfxWinTerm结束程序.

本文地址:http://com.8s8s.com/it/it1253.htm