Customize an IE Context Menu to Add CodeGuru Favorites

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Contents

Introduction

Being a frequent visitor of CodeGuru forums, I have come across questions that are oft-repeated and have already been answered in detail previously. At times like these, I have struggled to search for that particular thread and found it difficult to locate, or consumes too much time to weed it out of the several threads shown by the search engine. I have always wanted to have a location where I could store these useful threads, and from this desire, came upon the idea of “Why not have all these useful links available as a quick text ready to be pasted onto the forum’s editor window? And, why not have this available at my fingertips, literally; in other words, available on the click of a mouse?” Something like what you see in the figure below:

I did some research and decided to go the route as explained here. Having done this research, I came up with a basic idea. I knew I needed to make some Registry entries to add the menu item, and I needed a simple script to perform the actions I needed to when these menu items are invoked.

Having done this, I came up with requirements for two menu items. I needed one capable of adding a given URL to my own set of favorites. I needed another menu capable of pasting my saved URLs to CodeGuru edit boxes while replying to forum posts.

About the Article and Attached Demo

This article is an attempt to provide a step-by-step guide to implementing a custom IE context menu. A knowledge of COM (Component Object Model) and ActiveX is a prerequisite and will help you understand certain portions. The sample you will develop here demonstrates just the approach and core code snippets, just enough to demonstrate concepts. The demo attached is, however, a more complete solution. It has implementation of saving the favorites to an XML file and loading them in from there. Having said this, you will notice that there is a disconnect between the attached sample and the article code. This is expected because the article is only a guide to customize an IE context menu and the attached sample is a polished solution whose foundation is still based off of the things discussed in the article. If you are interested in just installing the component and using it, please proceed to the “Installation and Usage” section.

Implementing a Custom Context Menu

Okay; it’s time to get started. All you need to see a custom menu item on an IE context menu is a bunch of Registry entries and a script.

You have pretty much covered the basics. That is all there is to adding custom menu item entries. You will now add the necessary script actions to do what you want. For this part, your goals are the following:

  • When clicking on Add to CodeGuru Favorites, you want to save the URL and the document title onto a file. I will leave out the details of saving to file for the demo, but, for this article, it suffices to simply be able to get this information and show it to the user.
  • Goal number two would be to do the reverse; in other words, when right-clicking on text boxes and selecting Show CodeGuru Favorites, you want to pop up a menu that has leaf items, and on selecting one of the items, you want to paste that text to the edit box.

Let me start by saying this. The goals can be achieved in many ways and probably be achieved entirely by using JavaScripting. However, I, being a C++ guy and having limited to no knowledge on JavaScript, decided to implement it by using C++ and COM. I decided to implement the two functionalities inside an ActiveX class. So, get started.

  • Using your IDE (VS2005 or VS 6.0), create a new workspace and select the ATL COM Appwizard. Select an appropriate location and set the project name as, say, “CodeGuruFavorites”. Simply finish the wizard selecting all defaults. Build.
    • If using VS6.0, navigate to the Insert menu. Select “New ATL Object”. Select controls category and select “Lite Control”. Click Next. For the shortname, specify “CGFavorites”. Click OK. Build.
    • If using VS2005, navigate to the Project menu. Select “Add class”. Select ATL. Select “ATL Control”. Click Add. For the shortname, specify “CGFavorites”. Click Finish. Build.
  • Go to classview, right-click on ICGFavorites, and select “Add Method” from the menu options. Type in ShowDefaultContextMenu for the method name. For parameters, type in the following: IDispatch* pDispatch, BSTR bstrTitle, BSTR bstrURL (in case of VS2005, you will have to add these parameters one by one). Hit OK/Finish. Build.
  • Similarly, add another method called ShowTextAreaContextMenu with just one parameter: IDispatch* pDispatch. Hit OK/Finish. Build.
  • Open CGFavorites.cpp and add the following code to ShowDefaultContextMenu implementation.
  • STDMETHODIMP CCGFavorites::ShowDefaultContextMenu(
       IDispatch *pDispatch, BSTR bstrTitle, BSTR bstrURL)
    {
       // TODO: Add your implementation code here
       ::MessageBoxW(NULL,bstrTitle, bstrURL,MB_OK);
       return S_OK;
    }
    
  • Open AddToCGFavorites.html and replace the script with the following:

  • <SCRIPT LANGUAGE=”JavaScript”>
    var parentwin = external.menuArguments;
    var doc = parentwin.document;
    var str = new String(parentwin.event.srcElement.name);
    var oFav = new ActiveXObject(“CodeguruFavorites.CGFavorites”);
    oFav.ShowDefaultContextMenu(parentwin,doc.title, doc.location);
    </SCRIPT>

  • Launch IE. Navigate to codeguru.com. Right-click and select “Add to Codeguru Favorites”. You should see a message box with the title and URL.
  • Awesome. You now have the first goal done. The MessageBox can be replaced by all sorts of fancy code to save the URL and document title to the file etc. etc.

Basically, this is all that is happening. When you right-click, IE looks at what custom menus need to be added for the current context. It then appends those menu items. When one of the custom menu items is clicked, it executes the script specified. Some of the important information is passed in as menuArguments, which is what you use to obtain the necessary information like URL, title, and window object.

Moving on to the second goal, as in showing a popup menu and pasting contents into the edit box. Start with showing a popup menu at the cursor location.

  • Start by adding the following code to create a popup menu with two sub menuitems and to show it using TrackPopupMenu API.
  • #include <exdisp.h>
    #include <shlguid.h>
    
    STDMETHODIMP CCGFavorites::ShowTextAreaContextMenu(IDispatch
                                                       *pDispatch)
    {
       // TODO: Add your implementation code here
       //create a popup menu
       HMENU hPopupMenu = CreatePopupMenu();
    
       //insert items
       InsertMenuW(hPopupMenu,0,MF_BYPOSITION,1000,L"First");
       InsertMenuW(hPopupMenu,1,MF_BYPOSITION,1001,L"Second");
    
       //get the hWnd of the browser window
       CComQIPtr<IServiceProvider> isp = pDispatch;
       CComQIPtr<IWebBrowser2> pBrowser2;
       isp->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2,
                         (void**)&pBrowser2);
    
       HWND  hWnd;
       IOleWindow* pWindow = NULL;
       if (SUCCEEDED(isp->QueryService(
                                       SID_SShellBrowser,
                                       IID_IOleWindow,
                                       (void**)&pWindow)))
       {
          if (SUCCEEDED(pWindow->GetWindow(&hwnd)))
          {
             // hwnd is the handle of TabWindowClass on IE7 and
             // above and is the browser window on earlier versions
          }
          pWindow->Release();
       }
    
       //show menu
       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;
    }
    

    Nothing much here. The only tricky parts are how you get the browser window handle. Note that this is what you use the pDispatch for. It’s a long-winded way of getting to the IWebBrowser2 interface and from there, obtain the HWND of the window. Build.

  • Open ShowCGFavorites.html and add the following code.
  • <SCRIPT LANGUAGE="JavaScript">
    var parentwin = external.menuArguments;
    var oFav = new ActiveXObject("CodeguruFavorites.CGFavorites");
    oFav.ShowTextAreaContextMenu(parentwin);
    </SCRIPT>
    
  • Launch IE. Navigate to, say, www.gmail.com and right-click on the username edit box. You should see a menu item, Show CodeGuru Favorites. Click on it. Oops… Nothing happens??
  • Not to worry. It appears that TrackPopupMenu is failing for some reason. I haven’t figured out why, but, my guess is that our TrackPopupMenu is called as a result of IE also calling track popupmenu.
  • To circumvent this, you do this. Post a user-defined message to the window and popup the menu in that. Well, how do you process it because you aren’t the one that created the window? Simple, by subclassing. You just subclass the hwnd, post your message, handle the message and show menu, and then unsubclass. The code below shows these changes:
  • WNDPROC fnOldWndProc;
    LRESULT CALLBACK SubclassWndProc(HWND hwnd,
       UINT uMsg,
       WPARAM wParam,
       LPARAM lParam
    )
    {
       //if it is our custom message for showing favorites list
       if (uMsg == (WM_APP + 1))
       {
          //show favorites menu
          //create a popup menu
          HMENU hPopupMenu = CreatePopupMenu();
    
          //insert items
          InsertMenuW(hPopupMenu,0,MF_BYPOSITION,1000,L"First");
          InsertMenuW(hPopupMenu,1,MF_BYPOSITION,1001,L"Second");
    
          //show menu
          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 CCGFavorites::ShowTextAreaContextMenu(IDispatch
       *pDispatch)
    {
       // TODO: Add your implementation code here
    
       //get the hWnd of the browser window
       CComQIPtr<IServiceProvider> isp = pDispatch;
       CComQIPtr<IWebBrowser2> pBrowser2;
       isp->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2,
          (void**)&pBrowser2);
    
       HWND  hWnd;
       IOleWindow* pWindow = NULL;
       if (SUCCEEDED(isp->QueryService(
                                       SID_SShellBrowser,
                                       IID_IOleWindow,
                                       (void**)&pWindow)))
       {
          if (SUCCEEDED(pWindow->GetWindow(&hwnd)))
          {
             // hwnd is the handle of TabWindowClass on IE7 and
             // above and is the browser window on earlier versions
          }
          pWindow->Release();
       }
    
       //subclass the window here so we can process custom message
       /to show menu
       fnOldWndProc = (WNDPROC)::SetWindowLong(hWnd,GWL_WNDPROC,
          (DWORD)SubclassWndProc);
    
       //post our own message to show menu
       ::PostMessage(hWnd, (WM_APP + 1), 0,0);
    
       //restore the old WndProc back
       ::SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)fnOldWndProc);
    
       return S_OK;
    }
    
  • Build. Launch IE. Navigate to, say, www.gmail.com and right-click on the username edit box. You should see a menu item, Show CodeGuru Favorites. Click on it. Oops… This time, the menu did appear, but just flashed and disappeared!!
  • Something is still not right. Possibly, it’s because you are posting a message and not waiting for it to complete. You can circumvent this hurdle by adding an event and wait for it until it finishes right after PostMessage. The idea is to create a manual reset event initially set to not-triggered and wait on it after PostMessage. The subclass procedure would set the event when it returns from TrackPopupMenu.
    Changes are as below:
  • WNDPROC fnOldWndProc;
    LRESULT CALLBACK SubclassWndProc(HWND hwnd,
       UINT uMsg,
       WPARAM wParam,
       LPARAM lParam
    )
    {
       //if it is our custom message for showing favorites list
       if (uMsg == (WM_APP + 1))
       {
          //show favorites menu
          //create a popup menu
          HMENU hPopupMenu = CreatePopupMenu();
    
          //insert items
          InsertMenuW(hPopupMenu,0,MF_BYPOSITION,1000,L"First");
          InsertMenuW(hPopupMenu,1,MF_BYPOSITION,1001,L"Second");
    
          //show menu
          POINT pt;
          GetCursorPos(&pt);
          int iSelection = ::TrackPopupMenu(hPopupMenu,
             TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD,
             pt.x,pt.y, 0,hwnd,NULL);
    
          //after showing menu, signal the event
          SetEvent((HANDLE)lParam);
    
          DestroyMenu(hPopupMenu);
    
          return 0;
       }
    
       return CallWindowProc(fnOldWndProc, hwnd, uMsg,
                             wParam, lParam);
    }
    
    STDMETHODIMP CCGFavorites::
       ShowTextAreaContextMenu(IDispatch *pDispatch)
    {
       // TODO: Add your implementation code here
    
       //get the hWnd of the browser window
       CComQIPtr<IServiceProvider> isp = pDispatch;
       CComQIPtr<IWebBrowser2> pBrowser2;
       isp->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2,
                         (void**)&pBrowser2);
    
       HWND  hWnd;
       IOleWindow* pWindow = NULL;
       if (SUCCEEDED(isp->QueryService(
                                       SID_SShellBrowser,
                                       IID_IOleWindow,
                                       (void**)&pWindow)))
       {
          if (SUCCEEDED(pWindow->GetWindow(&hwnd)))
          {
             // hwnd is the handle of TabWindowClass on IE7 and
             // above and is the browser window on earlier versions
          }
          pWindow->Release();
       }
       HANDLE hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
    
       //subclass the window here so we can process custom message
       //to show menu
       fnOldWndProc = (WNDPROC)::SetWindowLong(hWnd,GWL_WNDPROC,
          (DWORD)SubclassWndProc);
    
       //post our own message to show menu
       ::PostMessage(hWnd, (WM_APP + 1), 0,(LPARAM)hEvent);
    
       //Wait for the event to be signalled, indicating menu
       //is gone
       WaitForSingleObject(hEvent,INFINITE);
    
       //cleanup
       CloseHandle(hEvent);
    
       //restore the old WndProc back
       ::SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)fnOldWndProc);
    
       return S_OK;
    }
    
  • Build. Perform the same steps and invoke the custom menu. There. That is much better. The menu is shown and it stays there for you to select. Click on it. Nothing happens. That is expected because you haven’t written that part yet.
  • To perform the paste operation, you can use the IWebBrowser2::ExecWB method passing in OLECMDID_PASTE as the command ID. How do you get this interface pointer in the Subclass procedure? Easy. You have this pointer already available in the showTextAreaContextMenu method. You simply have to pass it as WPARAM for the message. Do that as shown below.
    Modiy the PostMessage code as demonstrated below:
  •    //post our own message to show menu
       ::PostMessage(hWnd, (WM_APP + 1), (WPARAM)pBrowser2.p,
          (LPARAM)hEvent);
    

    Add this, after TrackPopupMenu call

    switch(iSelection)
    {
    case 1000:
       {
          CComBSTR oText(L"First one");
          CComVariant oVarIn(oText);
          CComVariant oVarOut;
          //get the IWebBrowser2 interface
          CComPtr<IWebBrowser2> 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;
          //get the IWebBrowser2 interface
          CComPtr<IWebBrowser2> pSp = (IWebBrowser2*)wParam;
          HRESULT hre = pSp->ExecWB(OLECMDID_PASTE,
             OLECMDEXECOPT_DODEFAULT,&oVarIn,&oVarOut);
       }
       break;
    }
    

    Nothing special here. You simply extract the IWebBrowser2 interface from the wParam and call ExecWB on it.

  • Build. And, repeat the test steps on an edit box. All is fine as long as you don’t select any menu item from your popup. As soon as you select one, you see an exception. Reason being, IWebBrowser2 interface has been sent across a thread boundary and per COM rules, this is a no-no. When such a situation arises, there are some procedures to follow. One way is to stream the interface; another simple procedure is to use a Global Interface Table. A Global Interface Table, or GIT for short, is a clever thing. It is a per-process entity. So, if multiple threads try to create one, the same instance is returned. It is like a bucket holding interfaces. If you want to share interface pointers across threads, you throw them into this GIT bucket. In return, you get a cookie back. You pass this cookie around to other threads and these threads in return will pass the cookie to the GIT to get a marshalled interface back. Simple. All you have to do now is to create such a GIT, throw your IWebBrowser2 interface in and pass the cookie to the subclass procedure instead of the interface pointer.
    With these changes, this is how the code looks:
  • WNDPROC fnOldWndProc;
    LRESULT CALLBACK SubclassWndProc(HWND hwnd,
       UINT uMsg,
       WPARAM wParam,
       LPARAM lParam
    )
    {
       //if it is our custom message for showing favorites list
       if (uMsg == (WM_APP + 1))
       {
          //show favorites menu
          //create a popup menu
          HMENU hPopupMenu = CreatePopupMenu();
    
          //insert items
          InsertMenuW(hPopupMenu,0,MF_BYPOSITION,1000,L"First");
          InsertMenuW(hPopupMenu,1,MF_BYPOSITION,1001,L"Second");
    
          //show menu
          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;
                //get the IWebBrowser2 interface
                CComPtr<IWebBrowser2> pSp;
                DWORD dwCookie = wParam;
                CComQIPtr<IGlobalInterfaceTable,
                   &IID_IGlobalInterfaceTable> 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;
                //get the IWebBrowser2 interface
                CComPtr<IWebBrowser2^gt; pSp;
                DWORD dwCookie = wParam;
                CComQIPtr<IGlobalInterfaceTable,
                   &IID_IGlobalInterfaceTable> 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;
          }
          //after showing menu, signal the event
          SetEvent((HANDLE)lParam);
    
          DestroyMenu(hPopupMenu);
    
          return 0;
       }
    
       return CallWindowProc(fnOldWndProc, hwnd, uMsg,
                             wParam, lParam);
    }
    
    STDMETHODIMP CCGFavorites::ShowTextAreaContextMenu(IDispatch
       *pDispatch)
    {
       // TODO: Add your implementation code here
       //create a GIT object
       //GIT is used to marshal the IWebBrowser2 interface pointer
       CComQIPtr<IGlobalInterfaceTable,
          &IID_IGlobalInterfaceTable> spGIT;
       CoCreateInstance(CLSID_StdGlobalInterfaceTable,NULL,
          CLSCTX_INPROC_SERVER,IID_IGlobalInterfaceTable,
          (void **)&spGIT);
    
       //get the hWnd of the browser window
       CComQIPtr<IServiceProvider> isp = pDispatch;
       CComQIPtr<IWebBrowser2> pBrowser2;
       isp->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2,
          (void**)&pBrowser2);
    
       //register interface in global
       DWORD dwCookie = 0;
       spGIT->RegisterInterfaceInGlobal(pBrowser2,
          IID_IWebBrowser2, &dwCookie);
    
       HWND  hWnd;
       IOleWindow* pWindow = NULL;
       if (SUCCEEDED(isp->QueryService(
                                       SID_SShellBrowser,
                                       IID_IOleWindow,
                                       (void**)&pWindow)))
       {
          if (SUCCEEDED(pWindow->GetWindow(&hwnd)))
          {
             // hwnd is the handle of TabWindowClass on IE7 and
             // above and is the browser window on earlier versions
          }
          pWindow->Release();
       }
    
       HANDLE hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
    
       //subclass the window here so we can process custom message
       /to show menu
       fnOldWndProc = (WNDPROC)::SetWindowLong(hWnd,GWL_WNDPROC,
          (DWORD)SubclassWndProc);
    
       //post our own message to show menu
       ::PostMessage(hWnd, (WM_APP + 1), (WPARAM)dwCookie,
          (LPARAM)hEvent);
    
       //Wait for the event to be signalled, indicating menu
       //is gone
       WaitForSingleObject(hEvent,INFINITE);
    
       //cleanup
       CloseHandle(hEvent);
    
       //restore the old WndProc back
       ::SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)fnOldWndProc);
    
       return S_OK;
    }
    
  • Build. And, repeat the test steps on an edit box. This time, you select the menu item and you no longer see an exception. However, IE is now just hung!! Why would that happen?
    This is what is happening. You will notice that, if you comment out the call to ExecWBm, all is fine. When you do call ExecWB, what happens is that it results in more window messages to the underlying window object used by COM. However, these aren’t getting processed because your ShowTextAreaContextMenu still hasn’t completed and is blocked on WaitForSingleObject. This results in a deadlock; hence, the IE lockup. What you need to solve this is to implement a mechanism of processing window messages while still remaining blocked on the hEvent. You have MsgWaitForMultipleObjects, which does exactly that. You introduce this code in your method now, as shown below. Simply replace the WaitForSingleObject line with this block of code:
  • //start loop
       while (TRUE)
       {
          // block-local variable
          DWORD result ;
          MSG msg ;
    
          // Read all of the messages in this next loop,
          // removing each message as we read it.
          while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
          {
             // Otherwise, dispatch the message.
             DispatchMessage(&msg);
          } // End of PeekMessage while loop.
    
          // Wait for any message sent or posted to this queue
          // or for one of the passed handles be set to signaled.
          result = MsgWaitForMultipleObjects(1, &hEvent,
                   FALSE, INFINITE, QS_ALLINPUT);
    
          // The result tells us the type of event we have.
          if (result == (WAIT_OBJECT_0 + 1))
          {
             // New messages have arrived.
             // Continue to the top of the always while loop to
             // dispatch them and resume waiting.
             continue;
          }
          else
          {
             // Our event was signalled .. time to quit loop
             break;
          } // End of else clause.
       }    // End of the always while loop.
    
  • Build and perform the test again. Voilà!! The test is now pasted on selecte menu items!!
  • Your main goals have been achieved. However, there is a small improvement that you can do. As of now, if you need to use this component, you need to ship the DLL, and also the script HTML files and the Registry entries. This is cumbersome. It would be cool if you could somehow encapsulate all this within the DLL and be done with it.
    Interestingly, this is possible and is fairly simple to achieve:
    • Go to resource view and to CodeGuru Favorites resources, right-click, and Add/Insert new resource. In the resulting dialog, select HTML and click on “Import”. Navigate to the location of the script HTML files and select it. Similarly, add the second HTML file too.
    • Open resource.h and note the values of IDR_HTML1 and IDR_HTML2.
    • Open the CGFavorites.rgs file and add the following to the end, replacing IDR_HTML1 and IDR_HTML2 by the values noted from resource.h:
    • HKCU
      {
         Software
         {
            Microsoft
            {
               'Internet Explorer'
               {
                  MenuExt
                  {
                     ForceRemove 'Add to Codeguru Favorites' =
                        s 'res://%MODULE%/IDR_HTML1'
                     {
                        val Contexts = d '1'
                     }
                     ForceRemove 'Show Codeguru Favorites' =
                        s 'res://%MODULE%/IDR_HTML2'
                     {
                        val Contexts = d '4'
                     }
                  }
               }
            }
         }
      }
      
    • Remove all the Registry entries you created manually at the beginning of the article. Launch IE again just to make sure the context menu items don’t appear anymore.
    • Now, just build the project and launch IE again. You now should see the context menu entries. Thus, with this approach, you have made all the capabilities self-contained within the DLL. Just registering the DLL on the target machine for the particular user is enough to populate the right entries.

Installation and Usage

To install and use, perform the following steps:

  • Unzip the compiled_binary.zip to a local folder. Place CGFavorites.dll in a location of your preference.
  • Launch cmd.exe from Start->Run.
  • Herein, type RegSvr32 [full path to the CGFavorites.dll]. For example, if you placed the DLL in C:\TempCG, your command will be
    RegSvr32 C:\TempCG\CGFavorites.dll

    You should see a message that you successfully registered.

  • Now, launch IE. Open an URL, say, https://www.codeguru.com/forum/showthread.php?t=365351. Right-click anywhere on the browser window to pop up the default context menu. Herein, you should see a new item, “Add to CodeGuru Favorites”. Select it. A dialog pops up, asking you to specify the description to use. You can modify it if needed, and then click OK. The page is now added to your CodeGuru favorites.
  • Hit Post reply on this page. You will be taken to an editor window. Herein, right-click. You should see a new menu item, Show CodeGuru Favorites, with submenus for articles and threads. Select the appropriate one and select any item. The URL and description should now be pasted at the cursor location in the editor window.
  • The favorites are stored into “My Documents” folder currently. The file stored in is CGFavorites.xml.

Update History

  • September 09, 2006: Context menus other than the CodeGuru favorites-related ones weren’t functional. Fixed this bug.
  • May 16, 2007: Rearchitected. No longer need Browser Helper Object. All implementation is per Microsoft recommendation and uses a combination of script code and an ActiveX control.
  • June 27, 2008: Fixed bug appearing on IE7 tabbed windows. Using IOleWindow instead of get_HWND.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read