Thinking in MFC---消息机制1

来源:互联网 发布:海信h168网络42寸彩电 编辑:程序博客网 时间:2024/06/05 06:04

Thinking in MFC---消息机制1

引言

在使用较长时间的MFC之后,感觉自己需要将零散的MFC知识整合一下,所以开始推出这个系列的博文,首先就从MFC经典的消息机制入手,来介绍MFC是怎么运作的。这篇主要介绍一下消息机制中几个基础概念。

这篇主要介绍消息如何路由到主窗口。

一、WinMain函数

写过win32程序,肯定只要我们要展示一个窗口,需要这个入口函数,这个就像控制台程序中main函数,是程序的入口。但是在MFC项目创建之后,我们在项目工程却找不到这个函数。

其实MFC已经将这个函数内部写好了,具体在下面这个文件中。

Microsoft Visual Studio10.0\VC\atlmfc\src\mfc\winmain.cpp

//

以下winmain函数中的内容,这些代码中的大部分我们暂时不做详细介绍,在之后的篇章中会来讨论这些代码。

 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)        {            TRACE(traceAppMsg,0, "Warning: Destroying non-NULLm_pMainWnd\n");            pThread->m_pMainWnd->DestroyWindow();        }        nReturnCode= pThread->ExitInstance();        goto InitFailure;    }    nReturnCode =pThread->Run(); InitFailure:#ifdef _DEBUG    // Check for missing AfxLockTempMap calls    if (AfxGetModuleThreadState()->m_nTempMapLock !=0)    {        TRACE(traceAppMsg,0, "Warning: Temp map lock count non-zero(%ld).\n",            AfxGetModuleThreadState()->m_nTempMapLock);    }    AfxLockTempMaps();    AfxUnlockTempMaps(-1);#endif     AfxWinTerm();    return nReturnCode;

上诉代码中第27行代码,我们需要找的消息循环就在Run函数中,接下来就看一下这个Run函数。

二、消息路由到主窗口

这个类微软用来封装MFC的线程。

这里我们主要来讨论这个类中的run函数

virtual int CWinThread::Run(){    ASSERT_VALID(this);    BOOL bIdle =TRUE;    LONGlIdleCount = 0;    //这里就是消息循环    for (;;)    {         // 如果线程处于空闲状态,消息队列中也没有消息,做空闲处理        while(bIdle && !::PeekMessage(&m_msgCur,NULL, NULL, NULL, PM_NOREMOVE))        {            // PeekMessage()用于检查消息队列是否为空,但并不处理消息            if (!OnIdle(lIdleCount++))                bIdle= FALSE;        }        do// 空闲处理接触,进行消息处理        {            if (!PumpMessage())// 消息处理,如果是WM_QUIT就退出消息循环                return ExitInstance();            if (IsIdleMessage(&m_msgCur))            {                bIdle= TRUE;                lIdleCount= 0;            }        } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL,PM_NOREMOVE));    }    ASSERT(FALSE);//}

这里先说明,以上代码包括下面的代码都是笔者从网上找到的,所以对于其准确性不能够保证,但是作为介绍性说明,应该是没有问题的。

我们按照函数执行顺序往下看,首先我们发现了一个死循环,这个死循环便是我们要的消息循环。

接下来我们便是idle状态的处理,没有消息处理的时候,线程进入idle状态。其中PeekMessage,便是用来判断消息队列中是否有消息。

再往下看,我们会发现一个叫PumpMessage,这个函数便是这里的一个关键函数(从run函数的代码中看,好像也没有其对队消息进行处理的函数了)。

接下来就看一下PumpMessage函数的源码

BOOL CWinThread::PumpMessage(){    ASSERT_VALID(this);    if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))        return FALSE;    if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))    {        ::TranslateMessage(&m_msgCur);        ::DispatchMessage(&m_msgCur);    }    return TRUE;}

按照执行顺序看,我们会注意到GetMessage,这个函数这里暂时不做介绍,就是从线程的消息队列中获取消息,并把消息内容放入一个结构的对象中(这里就放入msgCur)。

接下来便是判断消息是否是idle消息,如果不是就交给PreTranslateMessage,这个函数又是一个关键函数,所以我们有必要再次去找到它的源码进行分析。

BOOL CWinThread::PreTranslateMessage(MSG* pMsg){    ASSERT_VALID(this);     // if this is a thread-message, short-circuit this function    if (pMsg->hwnd == NULL &&DispatchThreadMessageEx(pMsg))        return TRUE;     // walk from target to main window    CWnd*pMainWnd = AfxGetMainWnd();    if(CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))        return TRUE;     // in case of modeless dialogs, last chance route throughmain    //   window'saccelerator table    if (pMainWnd != NULL)    {        CWnd*pWnd = CWnd::FromHandle(pMsg->hwnd);        if (pWnd->GetTopLevelParent() != pMainWnd)            return pMainWnd->PreTranslateMessage(pMsg);    }     return FALSE;  // no special processing}

我们按照函数的执行顺序来看,首先判断一下这个消息是否是发给窗口的。如果不是,那么就是一个线程消息,就交给DispatchThreadMessageEx(这里也暂时不介绍这个函数,不是这篇讨论的重点)。

接下面,我们获得主窗口对象,就把消息交给WalkPreTranslateTree处理。

继续探讨,我们有需要找到WalkPreTranslateTree的源码。(很抱歉,要看这么多代码)

BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop,MSG* pMsg){    ASSERT(hWndStop== NULL || ::IsWindow(hWndStop));    ASSERT(pMsg!= NULL);    // walk from the target window up to the hWndStop windowchecking    // if any window wants to translate this message    for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd =::GetParent(hWnd))    {        CWnd*pWnd = CWnd::FromHandlePermanent(hWnd);        if (pWnd != NULL)        {            // target window is a C++ window            if (pWnd->PreTranslateMessage(pMsg))                return TRUE; // trapped bytarget window (eg: accelerators)        }        // got to hWndStop window without interest        if (hWnd == hWndStop)             break;    }    return FALSE; // no specialprocessing}

略去一些空值的检查,我们看到消息最后是交给了主窗口的PreTranslateMessage函数处理了。接下来就交给主窗口进行消息处理了。

这里我们就大致了解了一个消息怎么路由到主窗口的流程。

三、总结

开始编程不久,便接触到了MFC。用了MFC写了自己第一个视窗程序,写了第一个游戏,在学校课题演示的时候也是喜欢用MFC做演示。

现在工作了,项目的界面部分也在使用MFC开发的,值得庆幸的是,现在微软对于MFC的界面做了很大的改善,想起刚开始不断重写控件类,那个真是记忆犹新。

在平时编程的时候,VS这个集成工具确实很方便。但现在想把一些MFC零零碎碎的知识整合起来。所以这个系列可能会深入地去介绍MFC。

 

 

 

 

 

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 脱贫攻坚怎么看怎么办怎么干 吃的下没力气怎么办 恢复留查公示后怎么办 微语简报删了怎么办 小学生未完成作业作为老师怎么办 抽调人员想回原单位怎么办 扫码支付没成功怎么办 成都电表卡丢了怎么办 社保未满15年怎么办 60岁社保没交满15年怎么办 柴油车dpf是亮了怎么办 贴双眼皮贴眼睛肿了怎么办 送孩子赴澳洲家长怎么办签证? 开指过程中发烧怎么办 大水口热流道有料花怎么办 奶奶疼外孙胜过孙子怎么办 吃了有病的鸡怎么办 博士真毕不了业怎么办 35岁博士毕不了业怎么办 跨校考研失败了怎么办 开发商把网签房卖给我该怎么办 cad中命令行没了怎么办 平板玩游戏很卡怎么办 卵巢包块20厘米怎么办 耳机的橡胶破了怎么办 脚被树枝扎破了怎么办 脚被木头扎肿了怎么办 手表字面脚断了怎么办 捡到苹果电脑怎么办才能自己用 耳机泡在水里了怎么办 蓝牙耳机泡水了怎么办 拖欠农民工工资劳动局不管怎么办 怎么办假的残疾军人证 外地人到北京就业怎么办五险 巴基斯坦留学生护照丢了怎么办 不敢上梯子太丢人怎么办 家人信了全能神怎么办 头皮上长淋巴炎怎么办? 鱼缸里的鱼相互咬怎么办? 火车凌晨4点到站怎么办 金立手机拍不了照怎么办