VC编程技术大型项目源码.doc_第1页
VC编程技术大型项目源码.doc_第2页
VC编程技术大型项目源码.doc_第3页
VC编程技术大型项目源码.doc_第4页
VC编程技术大型项目源码.doc_第5页
已阅读5页,还剩160页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

MFC消息机制首先,让我们看一下MFC的消息循环部分:(程序取自MFC源程序,由于篇幅,我删去了一些非重要的部分。)MFC的WinMain函数:cextern C int WINAPI_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)/ call shared/exported WinMainreturn AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)int nReturnCode = -1;CWinApp* pApp = AfxGetApp(); / / Perform specific initializationsif (!pApp-InitInstance()/ /初始化实例不成功,通常一个Dialog Based MFC程序必须返回FALSE/这样就可以跳过消息循环。nReturnCode = pApp-ExitInstance();goto InitFailure; nReturnCode = pApp-Run(); /进入消息循环部分 InitFailure:/ / 程序结束AfxWinTerm();return nReturnCode; int CWinApp:Run()/ return CWinThread:Run(); / 消息循环被封装在CWinThread类里。 int CWinThread:Run()BOOL bIdle = TRUE;LONG lIdleCount = 0; / 死循环,只有收到 WM_QUIT 消息后才会退出。for (;)while (bIdle &!:PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)if (!OnIdle(lIdleCount+)bIdle = FALSE;/ 如果消息队列中没有消息,那么就调用OnIdle函数/ 否则,发送消息doif (!PumpMessage()/ PumpMessage函数仅在收到WM_QUIT消息才返回FALSEreturn ExitInstance(); / 退出死循环 if (IsIdleMessage(&m_msgCur)bIdle = TRUE;lIdleCount = 0; while (:PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE);/ 这段程序不仅完成了消息的发送,还实现了Idle功能。/ GetMessage函数在消息队列中没有消息时,将不会返回,/ 而是将控制权交给操作系统,直到消息队列中有消息为止。/ 这段程序在一开始就调用PeekMessage函数来检测消息队列中/ 是否有消息存在,如果存在就发送消息,/ 否则就意味着空闲,那么就调用OnIdle函数,/ 这样做,控制权永远不会交给操作系统。/ 由于Windows 95, NT都是抢占式的操作系统,/ 系统会自动进行任务切换。/ 所以不用担心别的程序不会被运行。 BOOL CWinThread:PumpMessage()if (!:GetMessage(&m_msgCur, NULL, NULL, NULL)/ 收到 WM_QUIT 消息,就返回 FALSE。return FALSE; / 否则就发送消息if (m_msgCur.message != WM_KICKIDLE & !PreTranslateMessage(&m_msgCur):TranslateMessage(&m_msgCur);:DispatchMessage(&m_msgCur);return TRUE;/c主程序的流程:text(程序开始)|vWinMain|vAfxWinMain|v FALSECWinApp:InitInstance- 退出程序|TRUE|vCWinApp:Run|vCWinThread:Run|OnIdle+|TRUE |+ | | | |FALSE | +|v(程序结束)/text现在,再让我们来看一下MFC的窗口是如何响应消息的。 我们先来看一段建立一个窗口的代码.cclass CMsgWnd : public CWndpublic:CMsgWnd() / ClassWizard generated virtual function overrides/AFX_VIRTUAL(CMsgWnd)virtual BOOL Create(CWnd* pParentWnd);/AFX_VIRTUAL virtual CMsgWnd() protected:/AFX_MSG(CMsgWnd)afx_msg void OnPaint();afx_msg void OnLButtonDown(UINT nFlags, CPoint point);/AFX_MSGDECLARE_MESSAGE_MAP(); BEGIN_MESSAGE_MAP(CMsgWnd, CWnd)/AFX_MSG_MAP(CMsgWnd)ON_WM_PAINT()ON_WM_LBUTTONDOWN()/AFX_MSG_MAPEND_MESSAGE_MAP() BOOL CMsgWnd:Create(CWnd *pParentWnd)return CWnd:Create(AfxRegisterWndClass(CS_DBLCLKS),Message Window, WS_VISIBLE|WS_CHILD, CRect(0,0,100,100),pParentWnd, 12345); void CMsgWnd:OnPaint()CPaintDC dc(this); / device context for painting dc.TextOut(0,0,hello); void CMsgWnd:OnLButtonDown(UINT nFlags, CPoint point)MessageBox(OnLButtonDown); CWnd:OnLButtonDown(nFlags, point);/c以上是一段标准的CWnd窗口类的子类实现代码.我想大家应该是可以看的懂的.注意到CMsgWnd类中有一句代码 DECLARE_MESSAGE_MAP()我们来看看这个宏是如何定义的:ctypedef void (AFX_MSG_CALL CCmdTarget:*AFX_PMSG)(void);/ 这是一个指向 CCmdTarget 类成员函数的指针类型. struct AFX_MSGMAP_ENTRY / 消息映射表之消息入口UINT nMessage; / 消息UINT nCode; / 控制码或者是 WM_NOTIFY 消息的通知码UINT nID; / 控件的ID,如果是窗口消息则为0UINT nLastID; / 如果是一个范围的消息,那么这是最后一个控件的IDUINT nSig; / 消息处理类型AFX_PMSG pfn; / 消息处理函数; struct AFX_MSGMAP / 消息映射表#ifdef _AFXDLL / 如果MFC是动态连接的,/ 就是编译时选择 Use MFC in a shared DLLconst AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();#else / MFC是静态连接的,编译时选择 Use MFC in a static library.const AFX_MSGMAP* pBaseMap;#endif/ 如果MFC是动态连接的, 就用pfnGetBaseMap函数返回基类的消息映射表/ 否则 pBaseBap 指向基类的消息映射表 const AFX_MSGMAP_ENTRY* lpEntries; / 指向消息入口的指针; #ifdef _AFXDLL / 如果MFC是动态连接的#define DECLARE_MESSAGE_MAP() private: static const AFX_MSGMAP_ENTRY _messageEntries; / 消息入口protected: static AFX_DATA const AFX_MSGMAP messageMap; / 消息映射表static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); / 该函数返回基类的消息映射表virtual const AFX_MSGMAP* GetMessageMap() const; / 该函数返回当前类的消息映射表 #else / 静态连接的#define DECLARE_MESSAGE_MAP() private: static const AFX_MSGMAP_ENTRY _messageEntries; protected: static AFX_DATA const AFX_MSGMAP messageMap; virtual const AFX_MSGMAP* GetMessageMap() const; #endif/ 以上这段代码实际上是嵌入在你的类中. 在 .CPP 文件中我们看到还有这段宏,BEGIN_MESSAGE_MAP(CMsgWnd, CWnd)/AFX_MSG_MAP(CMsgWnd)ON_WM_PAINT()ON_WM_LBUTTONDOWN()/AFX_MSG_MAPEND_MESSAGE_MAP()现在我们再来看看它是被如何定义的. #ifdef _AFXDLL#define BEGIN_MESSAGE_MAP(theClass, baseClass) const AFX_MSGMAP* PASCAL theClass:_GetBaseMessageMap() return &baseClass:messageMap; / 返回基类的消息映射表const AFX_MSGMAP* theClass:GetMessageMap() const return &theClass:messageMap; / 返回当前类的消息映射表 AFX_DATADEF const AFX_MSGMAP theClass:messageMap = / 消息映射表 &theClass:_GetBaseMessageMap, &theClass:_messageEntries0 ; const AFX_MSGMAP_ENTRY theClass:_messageEntries = #else#define BEGIN_MESSAGE_MAP(theClass, baseClass) const AFX_MSGMAP* theClass:GetMessageMap() const return &theClass:messageMap; / 返回当前类的消息映射表 AFX_DATADEF const AFX_MSGMAP theClass:messageMap = / 消息映射表 &baseClass:messageMap, &theClass:_messageEntries0 ; 可以看到这是 MFC 动态连接与静态连接的区别所在 动态连接时使用函数 _GetBaseMessageMap 返回 &baseClass:messageMap而静态连接是直接使用.至于Microsoft为什么要这样做,好像没有什么很好的理由.当然这个并不重要,我们暂且不用理会. const AFX_MSGMAP_ENTRY theClass:_messageEntries = /开始初始化消息入口 #endif #define END_MESSAGE_MAP() 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 ; 指示消息映射结束. 在这两个宏之间还有ON_WM_PAINT()ON_WM_LBUTTONDOWN()它们是我们在ClassWizard中选择了WM_PAINT和WM_LBUTTONDOWN消息后,MFC自动加入的,那么这两个又是如何定义的呢? #define ON_WM_PAINT() WM_PAINT, 0, 0, 0, AfxSig_vv, | | | | +- 消息处理类型| | | +- LastID| | +- ID=0, 窗口消息| +- 控制码+ WM_PAINT 消息(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd:*)(void)&OnPaint , 消息处理函数. #define ON_WM_LBUTTONDOWN() WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, WM_LBUTTON 消息(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd:*)(UINT, CPoint)&OnLButtonDown , 消息处理函数 现在我们差不多可以看得出来了,消息处理函数可以靠_messageEntries来找到每个消息的处理函数.我们可以再看看CWnd类来验证我们的想法.先看一下窗口的建立过程:BOOL CWnd:Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,const RECT& rect,CWnd* pParentWnd, UINT nID,CCreateContext* pContext)/ cant use for desktop or pop-up windows (use CreateEx instead)ASSERT(pParentWnd != NULL);ASSERT(dwStyle & WS_POPUP) = 0); return CreateEx(0, lpszClassName, lpszWindowName,dwStyle | WS_CHILD,rect.left, rect.top,rect.right rect.left, rect.bottom rect.top,pParentWnd-GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext); 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 parametersCREATESTRUCT cs;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); if (!AfxUnhookWindowCreate()PostNcDestroy(); / cleanup if CreateWindowEx fails too soon if (hWnd = NULL)return FALSE;ASSERT(hWnd = m_hWnd); / should have been set in send msg hookreturn TRUE; / for child windowsBOOL CWnd:PreCreateWindow(CREATESTRUCT& cs)if (cs.lpszClass = NULL)/ make sure the default window class is registeredif (!AfxDeferRegisterClass(AFX_WND_REG)return FALSE; / no WNDCLASS provided use child window defaultASSERT(cs.style & WS_CHILD);cs.lpszClass = _afxWnd;return TRUE; void AFXAPI AfxHookWindowCreate(CWnd* pWnd)_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();if (pThreadState-m_pWndInit = pWnd)return; if (pThreadState-m_hHookOldCbtFilter = NULL)pThreadState-m_hHookOldCbtFilter = :SetWindowsHookEx(WH_CBT,Computer-based Training,当建立、删除、移动、最大化、最小化窗口时,将会调用钩子函数。_AfxCbtFilterHook, NULL, :GetCurrentThreadId(); 钩子函数if (pThreadState-m_hHookOldCbtFilter = NULL)AfxThrowMemoryException();ASSERT(pThreadState-m_hHookOldCbtFilter != NULL);ASSERT(pWnd != NULL);ASSERT(pWnd-m_hWnd = NULL); / only do once ASSERT(pThreadState-m_pWndInit = NULL); / hook not already in progresspThreadState-m_pWndInit = pWnd; BOOL AFXAPI AfxUnhookWindowCreate()_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();#ifndef _AFXDLLif (afxContextIsDLL & pThreadState-m_hHookOldCbtFilter != NULL):UnhookWindowsHookEx(pThreadState-m_hHookOldCbtFilter);删除钩子函数pThreadState-m_hHookOldCbtFilter = NULL;#endifif (pThreadState-m_pWndInit != NULL)pThreadState-m_pWndInit = NULL;return FALSE; / was not successfully hookedreturn TRUE; / Window creation hooksLRESULT CALLBACK_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();if (code != HCBT_CREATEWND) 是要建立窗口吗?/ wait for HCBT_CREATEWND just pass others onreturn CallNextHookEx(pThreadState-m_hHookOldCbtFilter, code,wParam, lParam); ASSERT(lParam != NULL);LPCREATESTRUCT lpcs = (LPCBT_CREATEWND)lParam)-lpcs;ASSERT(lpcs != NULL); / this hook exists to set the SendMessage hook on window creations/ (but this is only done for MFC windows or non-child windows)/ the subclassing cannot be done at this point because on Win32s/ the window does not have the WNDPROC set yetCWnd* pWndInit = pThreadState-m_pWndInit;if (pWndInit != NULL | (!(lpcs-style & WS_CHILD) & !afxContextIsDLL)ASSERT(wParam != NULL); / should be non-NULL HWNDHWND hWnd = (HWND)wParam;WNDPROC oldWndProc;if (pWndInit != NULL) 窗口建立来自一个CWnd?/ the window should not be in the permanent map at this timeASSERT(CWnd:FromHandlePermanent(hWnd) = NULL); / connect the HWND to pWndInitpWndInit-Attach(hWnd);/ allow other subclassing to occur firstpWndInit-PreSubclassWindow(); WNDPROC *pOldWndProc = pWndInit-GetSuperWndProcAddr();ASSERT(pOldWndProc != NULL); #ifndef _MAC_AFX_CTL3D_STATE* pCtl3dState;DWORD dwFlags;if (!afxData.bWin4 & !afxContextIsDLL &(pCtl3dState = _afxCtl3dState.GetDataNA() != NULL &pCtl3dState-m_pfnSubclassDlgEx != NULL &(dwFlags = AfxCallWndProc(pWndInit, hWnd, WM_QUERY3DCONTROLS) != 0) / was the class registered with AfxWndProc?WNDPROC afxWndProc = AfxGetAfxWndProc();函数见后BOOL bAfxWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC) = afxWndProc); pCtl3dState-m_pfnSubclassDlgEx(hWnd, dwFlags); / subclass the window if not already wired to AfxWndProcif (!bAfxWndProc)/ subclass the window with standard AfxWndProcoldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)afxWndProc); / 修改窗口的WndProc! ASSERT(oldWndProc != NULL);*pOldWndProc = oldWndProc;else#endif/ subclass the window with standard AfxWndProcWNDPROC afxWndProc = AfxGetAfxWndProc();oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)afxWndProc);ASSERT(oldWndProc != NULL);if (oldWndProc != afxWndProc)*pOldWndProc = oldWndProc;pThreadState-m_pWndInit = NULL;elseASSERT(!afxContextIsDLL); / should never get here / subclass the window with the proc which does gray backgroundsoldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);if (oldWndProc != NULL)ASSERT(GetProp(hWnd, szAfxOldWndProc) = NULL);SetProp(hWnd, szAfxOldWndProc, oldWndProc);if (WNDPROC)GetProp(hWnd, szAfxOldWndProc) = oldWndProc)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)(pThreadState-m_bDlgCreate ?_AfxGrayBackgroundWndProc : _AfxActivationWndProc);ASSERT(oldWndProc != NULL); LRESULT lResult = CallNextHookEx(pThreadState-m_hHookOldCbtFilter, code,wParam, lParam); #ifndef _AFXDLLif (afxContextIsDLL):UnhookWindowsHookEx(pThreadState-m_hHookOldCbtFilter);pThreadState-m_hHookOldCbtFilter = NULL;#endifreturn lResult; / always indirectly accessed via AfxGetAfxWndProcWNDPROC AFXAPI AfxGetAfxWndProc()#ifdef _AFXDLLreturn AfxGetModuleState()-m_pfnAfxWndProc;/ 如果MFC是动态连入,返回的地址是AfxWndProcBase#elsereturn &AfxWndProc; / 否则返回AfxWndProc#endif / The WndProc for all CWnds and derived classes LRESULT CALLBACKAfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)/ special message which identifies the window as using AfxWndProcif (nMsg = WM_QUERYAFXWNDPROC) 查询是否为AFX的窗口过程return 1; / all other messages route through message mapCWnd* pWnd = CWnd:FromHandlePermanent(hWnd);FromHandlePermanent函数用于返回一个和hWnd句柄对应的永久的CWnd类,所谓永久的,就是CWnd调用了Attach函数连上了一个窗口句柄。 ASSERT(pWnd != NULL);ASSERT(pWnd-m_hWnd = hWnd);return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); 函数见后 #ifdef _AFXDLL#undef AfxWndProcLRESULT CALLBACKAfxWndProcBase(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)AFX_MANAGE_STATE(_afxBaseModuleState.GetData();/ 由于MFC是动态连入的,所以必须要这条语句!return AfxWndProc(hWnd, nMsg, wParam, lParam);调用MFC静态连入时使用的函数#endif / 微软官方规定的发送消息给CWnd的函数LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,WPARAM wParam = 0, LPARAM lParam = 0)_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();MSG oldState = pThreadState-m_lastSentMsg; / save for nestingpThreadState-m_lastSentMsg.hwnd = hWnd;pThreadState-m_lastSentMsg.message = nMsg;pThreadState-m_lastSentMsg.wParam = wParam;pThreadState-m_lastSentMsg.lParam = lParam; #ifdef _DEBUGif (afxTraceFlags & traceWinMsg)_AfxTraceMsg(_T(WndProc), &pThreadState-m_lastSentMsg);#endif / Catch exceptions thrown outside the scope of a callback/ in debug builds and warn the user.LRESULT lResult;TRY#ifndef _AFX_NO_OCC_SUPPORT/ special case for WM_DESTROYif (nMsg = WM_DESTROY) & (pWnd-m_pCtrlCont != NULL)pWnd-m_pCtrlCont-OnUIActivate(NULL);#endif / special case for WM_INITDIALOGCRect rectOld;DWORD dwStyle;if (nMsg = WM_INITDIALOG)_AfxPreInitDialog(pWnd, &rectOld, &dwStyle); / delegate to objects WindowProclResult = pWnd-WindowProc(nMsg, wParam, lParam);调用CWnd类的WindowProc函数,函数见后 / more special case for WM_INITDIALOGif (nMsg = WM_INITDIALOG)_AfxPostInitDialog(pWnd, rectOld, dwStyle);CATCH_ALL(e)lResult = AfxGetThread()-ProcessWndProcException(e, &pThreadState-m_lastSentMsg);TRACE1(Warning: Uncaught exception in WindowProc (returning %ld).n,lResult);DELETE_EXCEPTION(e);END_CATCH_ALL pThreadState-m_lastSentMsg = oldState;return lResult;消息处理转入CWnd类了, LRESULT CWnd:WindowProc(UINT message, WPARAM wParam, LPARAM lParam) / OnWndMsg does most of the work, except for DefWindowProc callLRESULT lResult = 0;if (!OnWndMsg(message, wParam, lParam, &lResult)处理窗口消息 lResult = DefWindowProc(message, wParam, lParam);如果没有处理,就调用DefWindowProc,呵呵,和我们以前用C写Windows程序一样!return lResult; BOOL CWnd:OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)LRESULT lResult = 0; / special case for commandsif (message = WM_COMMAND) WM_COMMAND消息?if (OnCommand(wParam, lParam) 调用OnCommand函数,这是一个虚函数lResult = 1;goto LReturnTrue;return FALSE; / special case for notifiesif (message = WM_NOTIFY) 通告消息?NMHDR* pNMHDR = (NMHDR*)lParam;if (pNMHDR-hwndFrom != NULL &OnNotify(wParam, lParam, &lResult)调用OnNotify函数,也是一个虚函数goto LReturnTrue;return FALSE; / special case for activationif (message = WM_ACTIVATE)_AfxHandleActivate(this, wParam, CWnd:FromHandle(HWND)lParam); / special case for set cursor HTERRORif (message = WM_SETCURSOR &_AfxHandleSetCursor(this, (short)LOWORD(lParam), HIWORD(lParam)lResult = 1;goto LReturnTrue; const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论