惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Simon Willison's Weblog
Simon Willison's Weblog
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
Last Week in AI
Last Week in AI
月光博客
月光博客
有赞技术团队
有赞技术团队
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
Martin Fowler
Martin Fowler
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Check Point Blog
U
Unit 42
The Register - Security
The Register - Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
D
DataBreaches.Net
WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
H
Hacker News: Front Page
Recent Announcements
Recent Announcements
C
CXSECURITY Database RSS Feed - CXSecurity.com
Latest news
Latest news
小众软件
小众软件
P
Palo Alto Networks Blog
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest
S
Secure Thoughts
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
O
OpenAI News
S
Securelist
云风的 BLOG
云风的 BLOG
H
Help Net Security
T
Troy Hunt's Blog

博客园 - 红马天下

【原创】改造rails使支持gb2312编码 【转载】Rails使用gb2312/gbk与utf8共存的解决方案 【原创】MBCS,Unicode,UTF8以及一个用C++读写UTF8文件的工具类 【原创】Asp.Net从Windows Server 2003 32bit迁移到Windows Server 2008 64bit的找不到组件 这辈子一定要做一件有意义的事 【转载】软件缺陷管理基本概念 【转载】软件配置管理中的分支 【转载】我的Lean & Agile(精简和敏捷)经历(这才是最佳实践) 【转载】微软的管理模式真的值得吹嘘么? 【转载】负载均衡技术全攻略 【转载】MySpace的六次重构 【转载】转一篇自己的书评,关于《重构》 【转载】避免软件重建失败的三种方案 【转载】ASP.NET AJAX进度条 【转载】快速、可伸缩和流式的AJAX代理--跨域持续内容分发 给CHtmlEditView添加自定义拖拽功能IDropTargetpdwEffectDROPEFFECT_COPY ole拖放的若干tips 【转载】让文档模板支持多文件扩展名 在eXtreme Toolkit Pro 11.2.1中使用中文
深入分析eXtreme Toolkit Toolbar的Command消息
红马天下 · 2008-04-25 · via 博客园 - 红马天下

    eXtreme Toolkit(以下简称XTP)的功能强大毋庸置疑,但是虽然号称与MFC完美兼容,很多地方仍然区别很大,当我将原来的MFC程序改用eXtreme Toolkit时,经常出现若干非常奇怪的Bug,非常讨厌!

    Toolbar大概是每个应用程序都必不可少的元素。XTP的Toolbar功能比mfc实现要强大的多,实现也比其复杂得多。分析其Command消息的前世今生,追踪CXTPControlButton代码:

void CXTPControlButton::OnClick(BOOL bKeyboard, CPoint pt)
{
    
if (!GetEnabled()) return;

    
if (IsCustomizeMode())
    
{
        m_pParent
->SetPopuped(-1);
        m_pParent
->SetSelected(-1);
        CustomizeStartDrag(pt);
        
return;
    }


    
if (!bKeyboard)
    
{
        
if (m_pParent->GetType() != xtpBarTypePopup)
            ClickToolBarButton();
    }

    
else
    
{
        OnExecute();
    }

}

    这里关键在于ClickToolBarButton()和OnExecute()两个函数。继续查看这两个函数,OnExecute()似乎重点在于处理ToolBarButton支持的一些额外功能,如菜单等。真正产生Command消息的是ClickToolBarButton()函数。继续分析这个函数,这个函数是其基类CXTPControl的成员:

void CXTPControl::ClickToolBarButton(CRect rcActiveRect)
{    
    
    
if (bExecuteOnTimer)
    
{
        m_pParent
->SetTimer(XTP_TID_CLICKTICK, m_nExecuteOnPressInterval, NULL);
        NotifyExecute(
this, pOwner);
    }


    
while (::GetCapture() == hWndCapture)
    
{
        
        
if (msg.message == WM_LBUTTONUP)
        
{
            bClick 
= m_bSelected && ((!pt.x && !pt.y) || rcActiveRect.PtInRect(pt));
            
break;
        }


        
if (m_pParent == NULL)
            
break;

        
if (msg.message == WM_TIMER && msg.wParam == XTP_TID_CLICKTICK)
        
{
            
if (m_bSelected)
            
{
                NotifyExecute(
this, pOwner);
            }

        }

        
    }

    
}

    这里没有细致的分析代码,感觉大意是在WM_LBUTTONDOWN后设置timer,然后在WM_LBUTTONUP后产生Click消息,执行NotifyExecute(this, pOwner)。这个函数是CXTPControl的成员,继续追踪这个函数:

AFX_INLINE void NotifyExecute(CXTPControl* pControl, CWnd* pOwner)
{
    NMXTPCONTROL tagNMCONTROL;
    
if (pControl->NotifySite(pOwner, CBN_XTP_EXECUTE, &tagNMCONTROL) == 0)
    
{
        pOwner
->SendMessage(WM_COMMAND, pControl->GetID());
    }

}

    至此,心里有个猜想了,敢情先发通知消息,如果未被处理,才发送Command消息。就在当前文件中寻找NotifySite函数:

LRESULT CXTPControl::NotifySite(CWnd* pSite, UINT code, NMXTPCONTROL* pNM)
{
    
if (pSite == 0)
    
{
        
if (!m_pParent)
            
return 0;

        pSite 
= m_pParent->GetOwnerSite();
    }


    pNM
->hdr.code = code ;
    pNM
->hdr.idFrom = GetID();
    pNM
->hdr.hwndFrom = 0;
    pNM
->pControl = this;

    LRESULT lResult 
= pSite->SendMessage(WM_XTP_COMMAND, GetID(), (LPARAM)pNM);

    
if (lResult || !m_pParent)
        
return lResult;

    AFX_NOTIFY notify;
    notify.pResult 
= &lResult;
    notify.pNMHDR 
= (NMHDR*)pNM;

    
if (pSite->OnCmdMsg(GetID(), MAKELONG(code, WM_NOTIFY), &notify, NULL))
    
{
        
return lResult;
    }


    
return 0;
}

    这里先发一个用户消息WM_XTP_COMMAND,如果不被处理,调用父窗口OnCmdMsg函数。WM_XTP_COMMAND消息的用途没有找到,只在定义文件的注释里看到文字“ActiveX commands”,莫非用于某种形式的ActiveX交互?不过没关系,调用OnCmdMsg才是重点。注意函数体中的code是传来的参数,值为CBN_XTP_EXECUTE,表达式MAKELONG(code, WM_NOTIFY)的值刚好为5111908,这个值记住,以后有用处。如果OnCmdMsg没有处理这个消息,则调用链回到函数NotifyExecute处,发送标准的Command消息。
    到现在,ToolBar的Command消息处理过程已经清晰了。一般情况下,这种实现与MFC框架实现兼容。但在某些特殊情况下,会出现非常莫名其妙的错误。我在一个程序中使用了CHtmlEditView这个类,并使用了处理标准的html编辑命令的宏,如下例:

DHTMLEDITING_CMD_ENTRY_TYPE(ID_BUTTON_BOLD, IDM_BOLD, AFX_UI_ELEMTYPE_CHECBOX)

    奇异的情况发生了,编辑器竟然“不响应”这个加粗命令了!但是同时,编辑器响应左对齐等命令。更奇怪的是,当处理超链接命令时,竟然弹出两个对话框!仔细分析调试并实验发现,并非没有响应命令,而是响应得多了点,每个命令响应了两次。查看CHtmlEditView::OnCmdMsg()函数:

BOOL CHtmlEditView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
    
// if it's not something we're intersted in, let it go to the base
    if (nCode < (int)CN_UPDATE_COMMAND_UI)
        
return CHtmlView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

    
// check for command availability
    BOOL bHasExecFunc = FALSE;
    UINT uiElemType 
= AFX_UI_ELEMTYPE_NORMAL;
    UINT dhtmlCmdID 
= GetDHtmlCommandMapping(nID, bHasExecFunc, uiElemType);
    
if (dhtmlCmdID == AFX_INVALID_DHTML_CMD_ID)
    
{
        
// No mapping for this command. Use normal routing
        return CHtmlView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
    }


    
long nStatus = QueryStatus(dhtmlCmdID);

    
if (nCode == CN_UPDATE_COMMAND_UI)
    
{
        
// just checking status
        CCmdUI *pUI = static_cast<CCmdUI*>(pExtra);
        
if (pUI)
        
{
            
if(!(nStatus & OLECMDF_LATCHED || nStatus & OLECMDF_ENABLED))
            
{
                pUI
->Enable(FALSE);
                
if (uiElemType & AFX_UI_ELEMTYPE_CHECBOX)
                
{
                    
if (nStatus & OLECMDF_LATCHED)
                        pUI
->SetCheck(TRUE);
                    
else
                        pUI
->SetCheck(FALSE);
                }

                
else if (uiElemType & AFX_UI_ELEMTYPE_RADIO)
                
{
                    
if (nStatus & OLECMDF_LATCHED)
                        pUI
->SetRadio(TRUE);
                    
else
                        pUI
->SetRadio(FALSE);
                }


            }

            
else
            
{
                pUI
->Enable(TRUE); // enable
                
// check to see if we need to do any other state
                
// stuff
                if (uiElemType & AFX_UI_ELEMTYPE_CHECBOX)
                
{
                    
if (nStatus & OLECMDF_LATCHED)
                        pUI
->SetCheck(TRUE);
                    
else
                        pUI
->SetCheck(FALSE);
                }

                
else if (uiElemType & AFX_UI_ELEMTYPE_RADIO)
                
{
                    
if (nStatus & OLECMDF_LATCHED)
                        pUI
->SetRadio(TRUE);
                    
else
                        pUI
->SetRadio(FALSE);
                }

            }

            
return TRUE;
        }

        
return FALSE;
    }


    
// querystatus for this DHTML command to make sure it is enabled
    if(!(nStatus & OLECMDF_LATCHED || nStatus & OLECMDF_ENABLED))
    
{

        
// trying to execute a disabled command
        TRACE(traceHtml, 0"Not executing disabled dhtml editing command %d", dhtmlCmdID);
        
return TRUE;
    }


    
if (bHasExecFunc)
    
{
        
return ExecHandler(nID);        
    }


    
return S_OK == ExecCommand(dhtmlCmdID, OLECMDEXECOPT_DODEFAULT, NULL, NULL) ? TRUE : FALSE;
}

    结合前面对Command消息的了解并分析函数(这里只是一句话,但是我找出问题足足花了两天时间,包括对Command消息的跟踪),发现了这个函数的一个Bug:它没有判断nCode的值。显然,仅应该在nCode=CN_COMMAND的时候才执行html编辑命令。由于这个Bug,函数会在ToolBar发现通知消息的时候执行一次编辑动作,在确实发送Command消息的时候再执行一次。解决的方法很简单,重载OnCmdMsg函数,剔除掉通知消息即可。
    顺便说一下,CXTPMDIFrameWnd竟然不带Menu的,用GetMenu取得的菜单为空!