定制IE的右键菜单体会
最近换了一份新工作,新工作的第一个任务就是定制IE。花了一点时间翻看了MSDN中关于浏览器的接口文档就开工了。首当其冲就是定制IE的右键菜单。关键在于IDocHostUIHandler接口,重载其ShowContextMenu方法以用于定制原有的菜单或者显示自己的菜单.将该接口的实现对象设置给浏览器,浏览器在显示右键菜单时就会调用自己实现的ShowContextMenu方法了。原理挺简单的,但是绝知此事要躬行的。
实现ShowContextMenu方法的部分很简单,你可以到SHDOCLC.DLL中加载原有的菜单项并加以定制(MSDN有示例),也可以完全创建一个新的菜单,只需返回S_OK以屏蔽浏览器自己的菜单显示即可。至此有个问题出现了,新添加的菜单项永远是灰色不可用状态。查MSDN也没查出个所以然,只好替换了浏览器的窗口过程,处理其显示菜单时的消息(WM_INITMENUPOPUP消息),使自己的菜单项处于可用状态。
将IDocHostUIHandler接口传递给浏览器有2种可选方式:
1.通过浏览器的ICustomDoc接口,调用其SetUIHandler方法将IDocHostUIHandler传给浏览器。此法很好用,也没什么副作用,可其只能传递IDocHostUIHandler接口,对于其他接口(如IDocHostShowUI)就无能为力了。而且这种方式需要在浏览器第一个文档加载完成时才能设置(由于要通过IHTMLDocument2接口查询ICustomDoc接口)。
2.实现IOleClientSite接口,通过浏览器的IOleObject::SetClientSite设置给浏览器,浏览器会通过客户提供的IOleClientSite接口查询IDocHostUIHandler接口和IDocHostShowUI接口等。
综合考虑我选择了第二种方式,一切完成之后,达到效果了.可无意间发现一个很严重的问题,浏览器中的元素(如编辑框)接收不到键盘输入了。这一切让人不知所措。分析了一天终于找出问题所在(但还不知道原因),我在调用IOleObject::SetClientSite设置之前已经导航过网页地址,设置之后导航到别的网页就出现这个问题。可我用ATL的CAxWindow来创建网页控件的,不导航到网页就无法获取到IWebBrowser2指针,更谈不上获取IOleObject接口来设置IDocHostUIHandler对象了。费了九牛二虎之力来研究源码,最终还是明白了怎么CAxWindow不导航到网页也能获取IWebBrowser2指针(都怪自己对CAxWindow不够熟练)
//创建控件
CAxWindow::Create( hWndParent, rcDefault, NULL, WS_CHILD | WS_VISIBLE| WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
//使控件变为IE控件
LPOLESTR lpszClsid = NULL;
::StringFromCLSID( CLSID_WebBrowser,&lpszClsid );
ATLASSERT( SUCCEEDED( CAxWindow::CreateControl(lpszClsid) ) );
::CoTaskMemFree( lpszClsid );
//初始化接口
CComPtr<IWebBrowser2> spWebBrowser2;
HRESULT hRet = CAxWindow::QueryControl(IID_IWebBrowser2, (void**)&spWebBrowser2);
if(SUCCEEDED(hRet))
{
...
}