Customize an IE Context Menu to Add CodeGuru Favorites

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, http://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.


Downloads

Comments

  • La ghd cambiar tus cabellos como su deseo

    Posted by rzscwj997 on 07/17/2013 02:07pm

    Si no puede determinar el modelo que usted quiere comprar GHD, voy a dar las características de cada modelo, y para tratar de resolver algunos de los problemas también se explica sobre el cuidado del cabello, tales como el uso frecuente de secadores, rizadores, tablero muy socavarlo! Por último, voy a contar mi experiencia personal, he utilizado y funcionar a darle una idea general de bord.Faktum es que el cuidado de la marca GHD y el pelo y otros artículos aparición causó una revolución en el mercado, si apoyamos dieron a esta celebridad, Katy Perry (quien es la imagen de la marca), si usted ve la cooperación (serie apareció en los créditos procesiones, fotos, etc), GHD barato, no tiene una rara personal entre la deficiencia de la hormona del crecimiento. En resumen, la placa de tiempo [url=http://cheapghdoutlet.jimdo.com/]planchas ghd[/url] Alisadores de pelo GHD son los últimos de una amplia gama de equipos Incluso mejor que se les paga para salvar el planeta! El gobierno británico está tratando de promover el reciclaje de productos obsoletos y defectuosos de las organizaciones y de los hombres y mujeres, y hay muchos cursos intensivos de dinero para hacerlo. También puede considerar la basura de coche o camión en las antiguas placas ghdque esquema fueron comprados por dinero en efectivo para un coche nuevo, muchas organizaciones están utilizando este vehículo para aumentar las ventas de productos. Posiblemente también monedas de oro para los anuncios y reciclaje de teléfonos móviles en la prensa nacional y en tv.I una palabra, la plancha de pelo GHD es la mejor opción para proteger tu cabello del daño, por lo que es más seguro, para descomponerse en el mercado. [url=http://comprar-ghd.manifo.com/]planchas ghd baratas[/url] Hoy en dia, hay muchas herramientas para que podamos hacer nuestro cabello más style.men la plancha ghd es nuestra mejor opción, ya que puede tener el pelo más sano y fashion.I sugieren se lava el cabello y secarlo completamente antes de uso de alisadores. Si usted utiliza el pelo o cualquier otro planchas de pelo, causarán daños reparables para el cabello que está húmeda o no completamente tør.Vi ofrecen muchos tipos de Babyliss baratos GHD, marca de plancha de pelo ghd baratas, nueva llegada, descuento planchas ghd, etc nuevos estilos, buen precio, varios colores, entrega rápida, orden de la pequeña cantidad accepteret.Efter mi opinión, que vuelve a alegrar por la noticia de que ghd está preparado para you.All la ghd es muy chi y barato con una calidad superior. son absolutamente el mejor producto en el mercado.

    Reply
  • Hvorfor sportsfolk det mest effektive valg bevægelse af jorden spiser hovedtelefoner

    Posted by msxtfh899 on 07/17/2013 01:17am

    Denne nye forretninger med salget af den seneste teknologiske udvikling og kvalitetsudvikling i beats af forskellige mærker er en ganske næring til en. Kompatibilitet er også en grund, der gør folk kører efter de nye versioner på markedet. Beats by Dre er en af de mest populære mærker, når det kommer til hovedtelefoner af højeste kvalitet. News viser, at de har et partnerskab med Apple om at levere et eksklusivt sæt hovedtelefoner der er helt mindre i antal, men ganske højt i prisklasse. Monster beats er også en anden vigtig mærke i serien. [urlhttp://beatsbydredanmark.webspawner.com/]Beats by Dre[/url] Udover alle andre hovedtelefoner par, beats Dre lyd præsentation er temmelig god, ikke som “mudrede” og “uklar” støjende lyde, som ofte er problemer med hver hovedtelefon. Nå, denne genre af hovedtelefon er meget godt for den elektroniske, hård rock, og midt tempo hip-hop musik, som helt sikkert kan rock you ingen tvivl om. billige beats by dre er meget til salg i forskellige musik-butikker og du kan købe dem fra enhver stikkontakt til pluk det til din musik dille. Nå, ingen tvivl om, at du kan have en god musik ved at have det, efter alt dette er ‘beats by Dre «og navnet i sig selv er alt for at angive dets kvalitet. Så ven, så hvis du er en af dem, der søger for at have hovedtelefoner til dit musiksystem eller spekulerer at købe det til din iPhone eller iPod, kan du placere din ordre online. Du kan købe denne genre af hovedtelefoner lige fra sidder fra din hjemmecomputer, logge på hjemmesiden at udforske Dre beats rækkevidde at smage essensen af musikken. [url=http://beatsbydrdredan.ucoz.com/]beats by dre billigt[/url] Mange musikelskere tror, Musik er noget, der flyder gennem nerverne, som er bogstaveligt talt sandt, trods alt, føler det samme, da de mener. Så hvis du er til musik på en seriøs måde, så hvordan du foretrækker at flyde igennem det? Nå, jeg tror den bedste lydkvalitet ville være dit første præferencer for at øge smagen af din yndlingsmusik. Nå, mange tror musik er den del af livet, så mener jeg det samme. Det er noget, svært at forklare, men det eneste, du kan gøre med det, er føler. Derfor, dette er hvad alt hvad du behøver, det fedeste og kvalitet dr. dre Horetelefoner til at føle det bedre.

    Reply
  • clarisonic mia selling in the ebay is merit to obtain

    Posted by iouwanzi on 06/06/2013 08:10am

    [url=http://www.miaclarisonicaustralia.org/clarisonic-mia]clarisonic mia[/url] Je voudrais que celui qui crée mon vos cheveux aspect extraordinaire, ainsi que que j’ai pu utilisation pour vous aider à redresser et faire de superbes boucles. C’est pour quelle raison mon conjoint et j’ai décidé de commander quelques autre ghd Gold styler.Qu’ils sont sérieusement incroyables, avec, on dirait qu’ils soient uniquement en conséquence substantielle dans un an ou deux, sauf si la rend plue ramasser la fonction ! Qu’ils sont dignes du plus abordable par opposition à travers Aus, ainsi que leur prix, très dernière longtemps. ghd Gold styler ont un instrument d’authentification très bon sur les sites Web pour vous assurer que vous avez une totale fiabilité fer plat. [url=http://www.miaclarisonicaustralia.org/clarisonic-mia]clarisonic mia[/url] Autour du chemin de température Clever spécial à la suite de produits et de solutions Belson, fers de golf ciselés, sèche grève, fers de golf, ainsi que de redressage fers de golf Samsung S8500 avoir un système qui gère le warm-up pour le style de cheveux bouclés. Un inconvénient pour aider à détecter les chiens Rottweiler qui est si normal que ça pourrait faire une sorte d’approche nettement plus lent votre World Wide Web, si un individu peut non seulement trouver le approprié dans votre cas immédiatement [url=http://www.miaclarisonicaustralia.org/clarisonic-pro]clarisonic pro[/url] Les producteurs primaires suivantes commencé pour sa bonne qualité merveilleuse de votre sèche-cheveux et fer à lisser est aujourd’hui célèbre également la distinction entre les femmes qui parfois vous avez besoin pour redresser votre chevelure afin d’éliminer ces types de problèmes ghd fer luxe Violet, à son tour, n’est pas un peu ne serait tout simplement pas seulement en possession du logiciel. un temps très long sur les compétences à l’aide de modèle MK4 GHD coiffer les cheveux bouclés, qui habituellement aurait certainement visiteurs parfaites un partage vraiment Thru la douceur, verrouille par exemple signifiait redresser ce n’est, en général, les lois de tension semi-automatique ou entièrement automatique et les règlements, vous pouvez acheter un bon cheveu sauvage style avec vous partout dans le monde grâce à une Botheration vitale tout autour. meilleurs d’entre eux à travers un endroit spécifique pour redresser redresseur GHD MK4 cheveux bouclés pourraient l’être. problème en effet, le fait qui se produit à l’aide d’un opérateur capable dvd, plus de points, le type choisi des méthodes simples pour vous aider à contrôler l’application correcte et

    Reply
  • La nueva marca GHD Styler RARE , dispositivo de estilo Ghd

    Posted by hanmeihm on 05/30/2013 04:51pm

    [url=http://www.planchasghdbaratasonline.com/]planchas ghd[/url] El principal culpable de la destruccin de pelo es el uso excesivo del estilo planchas ghd. Cuando se utiliza demasiadas herramientas y productos para peinar, que han perdido su forma natural y rebote. Una cosa que a menudo le ayudar a mantener su definicin es simplemente cortar y simplificar la rutina de modelado. [url=http://www.ghd-baratas.webnode.es/]GHD BARATAS[/url] Si alguna vez se instal un medidor de temperatura fabulosa (no probarlo en su propia casa) en partes distintas dentro de la dentadura en planchas para el pelo la competencia que probablemente tenga en cuenta que normalmente el rango de calor puede variar considerablemente segn el lugar donde se coloca. Tpicamente, la plancha ghd posee un elemento de calentamiento matricial una parte de este para ser cierta distribucin muy posiblemente calor sobre la dentadura postiza. [url=http://www.planchasespanaghdtop.net/ghdtienda/]GHD Plancha de pelo[/url] Antes de cualquier estilo de calor, a menudo comienzan con una muy buena seguridad suero calor o aerosol. Cabello est¢ en su sano cuando hidratada y tenacillas para rizar, secadores de pelo y planchas para el pelo a todo seco y se deja sentir en affliction.It psimo es muy importante utilizar un buen par de buena calidad de las planchas cuando esperando para rizar el cabello. De lo contrario los alisadores pueden tal vez no te queme suficiente y mantenerse a una temperatura constante por mucho tiempo suficiente para realizar el mejor resultado. Usted puede obtener un par de planchas ghd baratos en lnea de una persona de los sitios de comparacin de costos ghd.

    Reply
  • Context menu closes when submenu displayed

    Posted by gcage on 05/29/2008 03:04pm

    Following the step by step instructions (they are excellent) and downloading the sample code I find that when the Show Codeguru Favorites context menu item is selected the context menu closes leaving the popup menu hanging in space. The popup menu functions correctly. How do I get the context menu to remain visible until the submenu item has been selected. By watching messages go by in SubclassWndProc it appears that the context menu is closed as a result of the WM_INITMENUPOPUP message.

    • Thanks for the clarification

      Posted by kirants on 09/21/2008 12:45pm

      I now see what you both are saying. Unfortunately, with the approach taken here, the menu will go away since, the script method is called as response to the menu and it returns immediately.

      Reply
    • I made a screencast

      Posted by waltersobchak on 09/21/2008 06:53am

      Well, its like gcage writes. The "Show Codeguru Favorites" menu entry does not contain submenus. Only when you click it, the original IE context menu disappears and the Codeguru Favorites menu with its subentries appears. Please see this screencast: http://screencast.com/t/dHHFW4wUWT

      Reply
    • More clarifications please

      Posted by kirants on 06/27/2008 08:27pm

      Hello, sorry for the delay. I am not clear on your findings. Could you please elaborate? Also, if you could turn on allowing me to contact you via email private messaging, that would help speed up communication. What IE version are you using , by the way ?

      Reply
    Reply
  • Launch a program with javascript?

    Posted by kras on 07/08/2007 08:40am

    How can i use javascript to open a program? I want to create a new entry in the context menu that opens a program and passes the right clicked url as an argument. Is that possible? regards Jimmie

    • Solution..

      Posted by kirants on 07/09/2007 08:30pm

      I am assuming you have read through the article on the basics of adding a context menu entry ( simply using the registry and adding a html file in a fixed folder location ). If you have done that and are able to get at least the message box, you can follow these steps. 
      
      For the Contexts entry, use hex 20 value. This means, to show the context menu when right clicked on an anchor tag. When this is done, and you select your menu item, the URL will can be extracted and passed on to an application ( using firefox for e.g.) like below for the javascript code:
      
      var parentwin = external.menuArguments;
      var doc = parentwin.document;
      var str = new String(parentwin.event.srcElement.href);
      v = new ActiveXObject("Shell.Application");
      
      v.ShellExecute("firefox.exe", str, "", "open", 10);
      

      Reply
    Reply
  • IWebBrowser2::Navigate does not work

    Posted by pkronk on 07/01/2007 12:53pm

    Hi, is there any possibility e.g. to navigate to a website instead of pasting the text? I tried using pSp->Navigate(L"www.myurl.com", ...); instead of pSp->ExecWB(OLECMDID_PASTE, ...); but it does not work. All IWebBrowser2 members which use navigation don't work (which are GoBack, GoForward, GoHome, Navigate, and Navigate2). There are no errors, just nothing happens. Any ideas? Thanks in advance, Peter

    Reply
  • very nice article

    Posted by dingo_kasper on 06/01/2007 07:27am

    i read it i read it... oho it's done .. i am very delightful to say that it is wonderful  and remarkable work
    
    dingo

    • Thank you

      Posted by kirants on 06/01/2007 12:12pm

      .. for your nice words. I am glad you found it interesting

      Reply
    Reply
  • Doesn't work in edit mode

    Posted by KFC123 on 04/13/2007 03:10am

    Hi there, I apply the code to the IE component at edit mode, when I click the item on encode menu, it clear the page!?

    Reply
  • How about vista?

    Posted by yecheng_110 on 03/09/2007 03:15am

    This is no file named "shdoclc.dll" in vista.

    • Should work now

      Posted by kirants on 05/23/2007 05:03pm

      Updated, actually re-architected , so should work on Vista now. Please let me know if you see a problem still

      Reply
    • Interesting..

      Posted by kirants on 03/09/2007 02:49pm

      Haven't tried it on Vista yet. Will do so and add comment. Thanks for pointing it out.

      Reply
    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: October 23, 2014 @ 12:00 p.m. ET / 9:00 a.m. PT Despite the current "virtualize everything" mentality, there are advantages to utilizing physical hardware for certain tasks. This is especially true for backups. In many cases, it is clearly in an organization's best interest to make use of physical, purpose-built backup appliances rather than relying on virtual backup software (VBA - Virtual Backup Appliances). Join us for this eSeminar to learn why physical appliances are preferable to …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds