为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

实例解析自定义IE右键上下文菜单

2010-08-17 11页 doc 58KB 23阅读

用户头像

is_731882

暂无简介

举报
实例解析自定义IE右键上下文菜单 实例解析自定义IE右键上下文菜单 打开注册表编辑器,定位至HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer,在这里,如果没有一个名为MenuExt的键值,就创建一个。在它下面,再创建一个键值,命名为“添加到收藏夹”。 现在,要来定义什么条件下显示这个菜单了,例如,想要它在单击链接时显示、或在主窗体而不是文本框中显示等等,这由一个“Contexts”值来决定——添加一个DWORD键值并命名为“Contexts”,设置值为1,表示它是默认上下文菜单。 ...
实例解析自定义IE右键上下文菜单
实例解析自定义IE右键上下文菜单 打开注册编辑器,定位至HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer,在这里,如果没有一个名为MenuExt的键值,就创建一个。在它下面,再创建一个键值,命名为“添加到收藏夹”。 现在,要来定义什么条件下显示这个菜单了,例如,想要它在单击链接时显示、或在主窗体而不是文本框中显示等等,这由一个“Contexts”值来决定——添加一个DWORD键值并命名为“Contexts”,设置值为1,表示它是默认上下文菜单。 设置好菜单项及何时显示之后,需要指定单击菜单后的动作,这可通过指定脚本的位置来完成,脚本在此可为JavaScript或VBScript。我们设置上下文菜单默认值为一个HTML文件的位置,如C:\tmp\AddToFavorites.html。在C:\tmp下,创建一个名为AddToFavorites.html的新文件,用记事本打开并输入以下代码: 这时再打开一个新的IE窗口,右键单击之后应该会看到“添加到收藏夹”菜单项,点击之后会弹出一个消息框“点击了‘添加到收藏夹’ . 同样,在MenuExt下再添加一个键值,如“显示收藏夹”,设置默认值为“C:\tmp\ShowFavorites.html”,添加一个名为“Contexts”的DWORD键值,并设为4,这里的4表示当右键单击控件时(如文本框)显示菜单项。 像前面一样,在C:\tmp下创建一个“ShowFavorites.html”文件,并输入以下代码: 基本概念已经讲完了,现在要添加必要的脚本动作了,分两步进行: Ø 当点击“添加到收藏夹”时,是想保存URL及文档标到文件中。 Ø 第二步刚好相反,也就是说,在文本框中右键单击并选择“显示收藏夹”时,应弹出一个菜单,可供选择其中一项,以便粘贴其中内容到文本框中。 看到这,可能会想到有很多种方法来完成上述目标,甚至完全使用JavaScript都可以,而在这我们是要用C++及COM来完成,也就是在一个ActiveX类中实现这两个功能。 打开Visual Studio IDE(VS2008、VS2005、或VS 6.0),创建一个新项目,选择“ATL COM Appwizard”,输入名字如“Favorites”,其他都以默认设置,点击完成,生成项目: 如果使用VS6.0,找到“插入(Insert)”菜单,选择“New ATL Object”,在控件种类(controls category)中选择“Lite Control”,点击下一步,对短名称(shortname),指定“Favorites”,点击OK,生成。 如果使用VS2008,找到“项目”菜单,选择“添加类”,选择ATL---->ATL控件,点击添加,对短名称,指定“Favorites”,点击完成,生成。 转到Classview,右键单击IFavorites选择“Add Method”,输入“ShowDefaultContextMenu”作为方法名,而对于参数,则输入:IDispatch* pDispatch, BSTR bstrTitle, BSTR bstrURL,(在VS2008中,这些参数需要一个一个添加),点击OK完成,生成。 同样地,再添加一个方法ShowTextAreaContextMenu,此时参数为IDispatch* pDispatch,点击OK完成,生成。 打开Favorites.cpp并添加下列代码到ShowDefaultContextMenu的实现中。 STDMETHODIMP CFavorites::ShowDefaultContextMenu( IDispatch *pDispatch, BSTR bstrTitle, BSTR bstrURL) { ::MessageBoxW(NULL,bstrTitle, bstrURL,MB_OK); return S_OK; } 打开AddToFavorites.html并用下列代码替换当中脚本: 运行IE,随意打开一个网站,右键单击并选择“添加到收藏夹”,应该会显示一个带有标题及URL的消息框。 好了,第一个目标已经完成。基本上来说,当右键单击时,IE会查看需要为当前上下文菜单添加点什么,这时就会追加上自定义的菜单项。其中某些重要的信息要在menuArguments中传递,如要获取的URL、标题、窗口对象。 来看第二个目标,即显示弹出菜单并传递相关内容到编辑框中。下面的代码就是创建了一个有两个子菜单的弹出菜单,并以TrackPopupMenu API显示出来。 #include STDMETHODIMP CCGFavorites::ShowTextAreaContextMenu(IDispatch *pDispatch) { //创建弹出菜单 HMENU hPopupMenu = CreatePopupMenu(); //插入相关菜单项 InsertMenuW(hPopupMenu,0,MF_BYPOSITION,1000,L"First"); InsertMenuW(hPopupMenu,1,MF_BYPOSITION,1001,L"Second"); //获取浏览器窗口的hWnd CComQIPtr isp = pDispatch; CComQIPtr pBrowser2; isp->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2, (void**)&pBrowser2); HWND hWnd; pBrowser2->get_HWND((long*)&hWnd); //显示菜单 POINT pt; GetCursorPos(&pt); int iSelection = ::TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, pt.x,pt.y, 0,hWnd,NULL); DestroyMenu(hPopupMenu); return S_OK; } 此处唯一要注意的是怎样获取浏览器句柄,这也是使用pDispatch的目的所在。接下来,打开ShowFavorites.html文件,并添加以下代码: 再打开IE,这次到www.gmail.com,在用户名输入框中右键单击,这时会有一个“显示收藏夹”的菜单,点击它,咦?怎么没反应?也许是我们调用TrackPopupMenu时,IE也在调用TrackPopupMenu。 怎么解决这个问题呢,因为我们不是窗口的创建者,那只有子类化了,先子类化hwnd,再粘贴所需信息,处理之后显示菜单,然后取消子类化,以下是修改后的代码: WNDPROC fnOldWndProc; LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { //判断是否为显示收藏夹列表的自定义消息 if (uMsg == (WM_APP + 1)) { HMENU hPopupMenu = CreatePopupMenu(); InsertMenuW(hPopupMenu,0,MF_BYPOSITION,1000,L"First"); InsertMenuW(hPopupMenu,1,MF_BYPOSITION,1001,L"Second"); POINT pt; GetCursorPos(&pt); int iSelection = ::TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, pt.x,pt.y, 0,hwnd,NULL); DestroyMenu(hPopupMenu); return 0; } return CallWindowProc(fnOldWndProc, hwnd, uMsg, wParam, lParam); } STDMETHODIMP CFavorites::ShowTextAreaContextMenu(IDispatch *pDispatch) { //获取浏览器窗口hWnd CComQIPtr isp = pDispatch; CComQIPtr pBrowser2; isp->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2, (void**)&pBrowser2); HWND hWnd; pBrowser2->get_HWND((long*)&hWnd); //在此子类化窗口以便能处理自定义消息来显示菜单 fnOldWndProc = (WNDPROC)::SetWindowLong(hWnd,GWL_WNDPROC, (DWORD)SubclassWndProc); ::PostMessage(hWnd, (WM_APP + 1), 0,0); //恢复原先的WndProc ::SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)fnOldWndProc); return S_OK; } 生成之后,打开IE来到www.gmail.com,在用户名输入框上右键单击,点击“显示收藏夹”,这一次,菜单出来了,但闪了一下就不见了。 还是不正确?可能是Post出消息之后没有等到它完成,再添加一个事件,并等PostMessage完成,思路大致是创建一个手工重置的事件,初始设为未引发,并在PostMessage之后等待它,子类化的过程当从TrackPopupMenu返回时将重置事件。下面是修改后的代码: WNDPROC fnOldWndProc; LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { if (uMsg == (WM_APP + 1)) { HMENU hPopupMenu = CreatePopupMenu(); InsertMenuW(hPopupMenu,0,MF_BYPOSITION,1000,L"First"); InsertMenuW(hPopupMenu,1,MF_BYPOSITION,1001,L"Second"); POINT pt; GetCursorPos(&pt); int iSelection = ::TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, pt.x,pt.y, 0,hwnd,NULL); //在显示菜单后,发信号给事件 SetEvent((HANDLE)lParam); DestroyMenu(hPopupMenu); return 0; } return CallWindowProc(fnOldWndProc, hwnd, uMsg, wParam, lParam); } STDMETHODIMP CFavorites:: ShowTextAreaContextMenu(IDispatch *pDispatch) { CComQIPtr isp = pDispatch; CComQIPtr pBrowser2; isp->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2, (void**)&pBrowser2); HWND hWnd; pBrowser2->get_HWND((long*)&hWnd); HANDLE hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); fnOldWndProc = (WNDPROC)::SetWindowLong(hWnd,GWL_WNDPROC, (DWORD)SubclassWndProc); ::PostMessage(hWnd, (WM_APP + 1), 0,(LPARAM)hEvent); //等待事件为有信号状态,此时表示菜单已显示 WaitForSingleObject(hEvent,INFINITE); CloseHandle(hEvent); ::SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)fnOldWndProc); return S_OK; } 生成后,执行前面相同的步骤,这次好多了,菜单显示出来后停留在那等待你选择,点击之后,无反应,当然了,因为还没有编写相关代码嘛。 要进行粘贴操作,可使用IWebBrowser2::ExecWB方法并传递进OLECMDID_PASTE作为命令ID,但我们在子类化过程中要怎样获得这个接口指针呢?其实很简单,这个指针已经在showTextAreaContextMenu方法中了,只需简单地把它作为WPARAM传递给消息就行了,如下所示: //发送自定义消息来显示菜单 ::PostMessage(hWnd, (WM_APP + 1), (WPARAM)pBrowser2.p, (LPARAM)hEvent); 在调用TrackPopupMenu之后,还需不回下列代码: switch(iSelection) { case 1000: { CComBSTR oText(L"First one"); CComVariant oVarIn(oText); CComVariant oVarOut; //取得IWebBrowser2接口 CComPtr pSp = (IWebBrowser2*)wParam; HRESULT hre = pSp->ExecWB(OLECMDID_PASTE, OLECMDEXECOPT_DODEFAULT,&oVarIn,&oVarOut); } break; case 1001: { CComBSTR oText(L"Second one"); CComVariant oVarIn(oText); CComVariant oVarOut; //取得IWebBrowser2接口 CComPtr pSp = (IWebBrowser2*)wParam; HRESULT hre = pSp->ExecWB(OLECMDID_PASTE, OLECMDEXECOPT_DODEFAULT,&oVarIn,&oVarOut); } break; } 这里只是从wParam中取出IWebBrowser2接口,并调用ExecWB。 生成之后,在编辑框中重复上述测试步骤,只要不点击任何弹出菜单,一切正常,一旦点击其中某个,就会看到一个异常。究其原因,是因为IWebBrowser2接口在线程边界间传递,而依据COM的原则,这是不允许的,所以如果非要这么做,这依照以下二种方法:一是对接口进行“流(Stream)”化;二是使用全局接口表,全局接口表(GIT)可跨越进程,其是一个进程间的实体对象,如果有多个线程想要创建一个全局接口表,只会返回全局接口表的同一个实例,它就像是一个“装满接口的桶子”,所以如果想在线程间共享接口指针,把它们放在全局接口表中就行了,之后你会得到一个cookie,可把这个cookie传递给其他线程,接着这些线程会把它传递给全局接口表,并得到一个封送(marshal)接口。现在,我们只需创建一个全局接口表,放入IWebBrowser2接口,这次把cookie而不是接口指针传递给子类化过程。 下面是修改后的代码: WNDPROC FNoLDwNDpROC; LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { if (uMsg == (WM_APP + 1)) { HMENU hPopupMenu = CreatePopupMenu(); InsertMenuW(hPopupMenu,0,MF_BYPOSITION,1000,L"First"); InsertMenuW(hPopupMenu,1,MF_BYPOSITION,1001,L"Second"); POINT pt; GetCursorPos(&pt); int iSelection = ::TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, pt.x,pt.y, 0,hwnd,NULL); switch(iSelection) { case 1000: { CComBSTR oText(L"First one"); CComVariant oVarIn(oText); CComVariant oVarOut; CComPtr pSp; DWORD dwCookie = wParam; CComQIPtr spGIT; CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,(void **)&spGIT); spGIT->GetInterfaceFromGlobal(dwCookie, IID_IWebBrowser2,(void**)&pSp); HRESULT hre = pSp->ExecWB(OLECMDID_PASTE, OLECMDEXECOPT_DODEFAULT,&oVarIn,&oVarOut); } break; case 1001: { CComBSTR oText(L"Second one"); CComVariant oVarIn(oText); CComVariant oVarOut; CComPtr pSp; DWORD dwCookie = wParam; CComQIPtr spGIT; CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,(void **)&spGIT); spGIT->GetInterfaceFromGlobal(dwCookie, IID_IWebBrowser2,(void**)&pSp); HRESULT hre = pSp->ExecWB(OLECMDID_PASTE, OLECMDEXECOPT_DODEFAULT,&oVarIn,&oVarOut); } break; } //显示菜单后,发信号给事件 SetEvent((HANDLE)lParam); DestroyMenu(hPopupMenu); return 0; } return CallWindowProc(fnOldWndProc, hwnd, uMsg, wParam, lParam); } STDMETHODIMP CFavorites::ShowTextAreaContextMenu(IDispatch *pDispatch) { //创建一个全局接口表对象 //全局接口表用于封送IWebBrowser2接口指针 CComQIPtr spGIT; CoCreateInstance(CLSID_StdGlobalInterfaceTable,NULL, CLSCTX_INPROC_SERVER,IID_IGlobalInterfaceTable, (void **)&spGIT); //取得浏览器窗口hWnd CComQIPtr isp = pDispatch; CComQIPtr pBrowser2; isp->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2, (void**)&pBrowser2); //注册全局接口 DWORD dwCookie = 0; spGIT->RegisterInterfaceInGlobal(pBrowser2, IID_IWebBrowser2, &dwCookie); HWND hWnd; pBrowser2->get_HWND((long*)&hWnd); HANDLE hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); //在此子类化窗口,以便处理自定义消息来显示菜单 fnOldWndProc = (WNDPROC)::SetWindowLong(hWnd,GWL_WNDPROC, (DWORD)SubclassWndProc); //发送自定义消息显示菜单 ::PostMessage(hWnd, (WM_APP + 1), (WPARAM)dwCookie, (LPARAM)hEvent); //等待事件为有信号状态,表明菜单已完成 WaitForSingleObject(hEvent,INFINITE); CloseHandle(hEvent); //恢复原有WndProc ::SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)fnOldWndProc); return S_OK; } 生成之后重复前述步骤,这一次在选择菜单后就没有异常发生了,然而,IE却失去响应了,这是怎么回事? 这次又是发生什么事了呢?你会发现,如果注释掉ExecWBm,一切正常,而当调用ExecWBm时,它向COM隐含使用的窗口对象发送了大量的窗口消息,然而,因ShowTextAreaContextMenu仍未完成并在等待WaitForSingleObject,致使这些消息没有被处理,形成死锁导致IE失去响应。解决办法是要当仍处于hEvent阻塞时,实现一种窗口消息处理机制,这时我们想到了MsgWaitForMultipleObjects,其刚好可满足上述条件。如下所示,用下列代码替换了WaitForSingleObject: //开始循环 while (TRUE) { //代码块中局部变量 DWORD result ; MSG msg ; //在循环中读取所有消息 //并移除我们读取过的每条消息 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {   DispatchMessage(&msg); } //等待任何发送到此队列中的消息 //或是等待传递进来一个已设为有信号状态的句柄 result = MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT); //result会告知我们事件的类型 if (result == (WAIT_OBJECT_0 + 1)) { //新消息已到达 //继续回到while循环开始 //以分发它们并重新开始等待 continue; } else { //我们的事件为有信号状态,是时候退出循环了 break; } } 生成后再试一次,这次选择菜单后,文本成功粘贴上去了,我们的主要目标已经完成了,可还有一点改进的地方,如果要使用这个组件,就需要把这个DLL分发出去,而且还有HTML文件及注册表项,如果能打包在一起就方便多了,其实也不难。 Ø 打开项目的资源视图,鼠标右键单击选择“添加/插入新资源”,选择HTML并点击“导入”,找到HTML文件并选择它,同样可添加第二个HTML文件。 Ø 打开resource.h,记下IDR_HTML1及IDR_HTML2的值。 Ø 打开Favorites.rgs文件并将以下内容添加到末尾,把IDR_HTML1及IDR_HTML2替换为刚才记下的值。 HKCU { Software { Microsoft { 'Internet Explorer' { MenuExt { ForceRemove '添加到收藏夹' = s 'res://%MODULE%/IDR_HTML1' { val Contexts = d '1' } ForceRemove '显示收藏夹' = s 'res://%MODULE%/IDR_HTML2' { val Contexts = d '4' } } } } } } 删除前面所有手工创建的注册表项,再次打开IE确认上下文菜单不会再显示。 现在,可以生成项目了,运行IE之后,应该会看到上下文菜单项了,这样就把所有的东西都包含在DLL中,往后只需注册这个DLL就可以了,如:RegSvr32 C:\Temp\Favorites.dll。 以后再登录论坛时,只需在浏览器窗口中右键单击,就可以看到一个弹出菜单,选择“添加到收藏夹”可把当前网址添加到收藏夹中;同样,在回复帖子时,在编辑窗口中右键单击,在“显示收藏夹”菜单下就有以前存过的项目,选择其一之后,URL及相关说明就会粘贴到编辑窗口的当前光标处,是不是又快又方便啊。 文章出处:飞诺网(http://www.diybl.com/course/3_program/c++/cppsl/2008611/124595_3.html)
/
本文档为【实例解析自定义IE右键上下文菜单】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索