12.2 Browser Extensions

only for RuBoard - do not distribute or recompile

12.2 Browser Extensions

We can take the code we have so far a step further . . . but only if you have Internet Explorer 5.0 installed on your box. IE 5.0 provides us with the means to add a menu item and toolbar button for our component. There is some grunt work involved, but it's not too bad. Basically, we have to add a bunch of settings to the registry, and our component needs to implement an additional interface, IOleCommandTarget .

We are also going to become somewhat familiar what another new interface called IServiceProvider . This is because we cannot query the site pointer for IWebBrowser2 like we did for BHOs. We will get access to an IServiceProvider interface that we can use to request the IWebBrowser2 . As you will see in Section 12.2.2 later in this chapter, we will have to jump through a few hoops to do this.

Just to keep things a little suspenseful, we'll wait until the component is wired up and ready to go before we actually discuss its purpose. Exciting, huh?

12.2.1 How Browser Extensions Work

For the most part, browser extensions work just like browser helper objects. But there are some minor differences. For one thing, they are registered differently. When Explorer loads a browser extension, it looks under the following key:

 HKEY_LOCAL_MACHINE\     SOFTWARE\         Microsoft\             InternetExplorer\                 Extensions\ 

There are also some additional registry entries that will provide the toolbar icons and the menu item text, but we'll discuss them later.

12.2.2 Browser Extension Interfaces

Like BHOs, browser extensions implement IObjectWithSite . They are also required to implement another interface called IOleCommandTarget . This interface is used to execute menu and toolbar commands. In addition to IOleCommandTarget , browser extensions must be aware of another interface called IServiceProvider . Browser extension are a little different than BHOs in that they cannot query for IE directly from the site pointer. Rather, they are given a pointer to an IServiceProvider interface. IServiceProvider , in turn , supplies the browser extension with a reference to IWebBrowser2 .

We've already discussed IObjectWithSite in the section on BHOs. In the following sections, we'll discuss the IOleCommandTarget and IServiceProvider interfaces.

12.2.2.1 IOleCommandTarget

In terms of browser extensions, IOleCommandTarget allows us to process menu and toolbar commands that we have added to Explorer. This interface contains two methods , which are described in Table 12.3.

Table12.3. IOleCommandTarget

Method

Description

QueryStatus

Returns the status of one or more commands generated by user -interface events.

Exec

Executes a command from the menu or the toolbar.

12.2.2.1.1 QueryStatus

The shell calls this method to determine which commands are supported by the BHO and which commands are enabled or disabled, and to provide the name or status of a command:

 HRESULT QueryStatus(GUID *   pguidCmdGroup   , ULONG   cCmds   ,                      OLECMD *   prgCmds   , OLECMDTEXT *   pCmdText   ); 

Its parameters are:

pguidCmdGroup

[in] A NULL for browser extensions.

cCmds

[in] The number of commands in the prgCmds array.

prgCmds

[in] An array of OLECMD structures that indicate the commands for which Explorer needs information. OLECMD is defined as follows :

 typedef struct _tagOLECMD {     ULONG cmdID;     DWORD cmdf; } OLECMD; 

Its members are as follows:

cmdID

This is the ID of one of Explorer's commands, which is taken from the following enumeration:

 typedef enum {     OLECMDID_OPEN               =  1,     OLECMDID_NEW                =  2,     OLECMDID_SAVE               =  3,     OLECMDID_SAVEAS             =  4,     OLECMDID_SAVECOPYAS         =  5,     OLECMDID_PRINT              =  6,     OLECMDID_PRINTPREVIEW       =  7,     OLECMDID_PAGESETUP          =  8,     OLECMDID_SPELL              =  9,     OLECMDID_PROPERTIES         = 10,     OLECMDID_CUT                = 11,     OLECMDID_COPY               = 12,     OLECMDID_PASTE              = 13,     OLECMDID_PASTESPECIAL       = 14,     OLECMDID_UNDO               = 15,     OLECMDID_REDO               = 16,     OLECMDID_SELECTALL          = 17,     OLECMDID_CLEARSELECTION     = 18,     OLECMDID_ZOOM               = 19,     OLECMDID_GETZOOMRANGE       = 20     OLECMDID_UPDATECOMMANDS     = 21     OLECMDID_REFRESH            = 22     OLECMDID_STOP               = 23     OLECMDID_HIDETOOLBARS       = 24     OLECMDID_SETPROGRESSMAX     = 25     OLECMDID_SETPROGRESSPOS     = 26     OLECMDID_SETPROGRESSTEXT    = 27     OLECMDID_SETTITLE           = 28     OLECMDID_SETDOWNLOADSTATE   = 29     OLECMDID_STOPDOWNLOAD       = 30     OLECMDID_ONTOOLBARACTIVATED = 31,     OLECMDID_FIND               = 32,     OLECMDID_DELETE             = 33,     OLECMDID_HTTPEQUIV          = 34,     OLECMDID_HTTPEQUIV_DONE     = 35,     OLECMDID_ENABLE_INTERACTION = 36,     OLECMDID_ONUNLOAD           = 37 } OLECMDID; 
cmdf

This is the type of support provided by the BHO for the command that is specified by cmdID . It can be one value from the following enumeration:

 typedef enum {     OLECMDF_SUPPORTED   =  1,     OLECMDF_ENABLED     =  2,     OLECMDF_LATCHED     =  4,     OLECMDF_NINCHED     =  8 } OLECMDF; 

The OME_CMDF_SUPPORTED and OLECMDF_ENABLED items are fairly obvious. OLECMDF_LATCHED means the command is an on-off toggle that is currently on. The meaning of OLECMD_NINCHED is somewhat mysterious . The Platform SDK says that the value is reserved for future use. However, the value supposedly represents the undefined state of a three-state control.

pCmdTest

[in] Pointer to an OLECMDTEXT structure that is used to return name/status information to Explorer for one command. This value can be NULL , indicating that the caller does not need the information. The structure is defined as follows:

 typedef struct _tagOLECMDTEXT {     DWORD cmdtextf;     ULONG cwActual;     ULONG cwBuf;     wchar_t rgwz[1]; }OLECMDTEXT; 

The members of OLECMDTEXT are:

cmdtextf

Value that determines whether the rgwz member contains status text or a command name. This value is defined in the OLECMDTEXTF enumeration:

 typedef enum {     OLECMDTEXTF_NONE   =  0,     OLECMDTEXTF_NAME   =  1,     OLECMDTEXTF_STATUS =  2 } OLECMDTEXTF; 
cwActual

The number of characters actually written to the rgwz buffer before QueryStatus returns.

cwBuf

The number of elements in the rgwz buffer.

rgwz

A wide-character buffer (two bytes per character) that receives the status text or command name.

12.2.2.1.2 Exec

This method is used to execute a command. It can also be used to display help for a command. Its syntax is:

 HRESULT Exec(GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt,               VARIANTARG *pvaIn, VARIANTARG *pvaOut); 

with the following parameters:

pguidCmdGroup

[in] A NULL for browser extensions.

nCmdID

[in] Command to be executed.

nCmdExecOpt

[in] One or more values from the OLECMDEXECOPT enumeration that describe how the command should be executed. This parameter should be ignored for browser extensions.

pvaIn

[in] Pointer to a VARIANTARG (same as a Variant) structure that contains input arguments. This parameter can also be NULL .

pvaOut

[in, out] Pointer to a VARIANTARG structure that returns command output. This parameter can also be NULL .

Example 12.10 contains the interface definition for IOleCommandTarget .

Example 12.10. IOleCommandTarget Interface
 [     uuid(B722BCCB-4E68-101B-A2BC-00AA00404770),     helpstring("IOleCommandTarget Interface"),     odl ] interface IOleCommandTarget : IUnknown {      typedef enum OLECMDF { OLECMDF_SUPPORTED       = 0x00000001, OLECMDF_ENABLED         = 0x00000002, OLECMDF_LATCHED         = 0x00000004, OLECMDF_NINCHED         = 0x00000008, } OLECMDF; typedef struct _tagOLECMD {         ULONG   cmdID;         DWORD   cmdf;     } OLECMD;     typedef struct _tagOLECMDTEXT{             DWORD cmdtextf;             ULONG cwActual;             ULONG cwBuf;                 wchar_t *rgwz;      } OLECMDTEXT; typedef enum OLECMDID {             OLECMDID_OPEN                           = 1,             OLECMDID_NEW                            = 2,             OLECMDID_SAVE                           = 3,             OLECMDID_SAVEAS                         = 4,             OLECMDID_SAVECOPYAS                     = 5,             OLECMDID_PRINT                          = 6,             OLECMDID_PRINTPREVIEW                   = 7,             OLECMDID_PAGESETUP                      = 8,             OLECMDID_SPELL                          = 9,             OLECMDID_PROPERTIES                     = 10,             OLECMDID_CUT                            = 11,             OLECMDID_COPY                           = 12,             OLECMDID_PASTE                          = 13,             OLECMDID_PASTESPECIAL                   = 14,             OLECMDID_UNDO                           = 15,             OLECMDID_REDO                           = 16,             OLECMDID_SELECTALL                      = 17,             OLECMDID_CLEARSELECTION                 = 18,             OLECMDID_ZOOM                           = 19,             OLECMDID_GETZOOMRANGE                   = 20,             OLECMDID_UPDATECOMMANDS                 = 21,             OLECMDID_REFRESH                        = 22,             OLECMDID_STOP                           = 23,             OLECMDID_HIDETOOLBARS                   = 24,             OLECMDID_SETPROGRESSMAX                 = 25,             OLECMDID_SETPROGRESSPOS                 = 26,             OLECMDID_SETPROGRESSTEXT                = 27,             OLECMDID_SETTITLE                       = 28,             OLECMDID_SETDOWNLOADSTATE               = 29,             OLECMDID_STOPDOWNLOAD                   = 30,             OLECMDID_ONTOOLBARACTIVATED             = 31,             OLECMDID_FIND                           = 32,             OLECMDID_DELETE                         = 33,             OLECMDID_HTTPEQUIV                      = 34,             OLECMDID_HTTPEQUIV_DONE                 = 35,             OLECMDID_ENABLE_INTERACTION             = 36,             OLECMDID_ONUNLOAD                       = 37,             OLECMDID_PROPERTYBAG2                   = 38,             OLECMDID_PREREFRESH                     = 39,     } OLECMDID; typedef enum OLECMDTEXTF {             OLECMDTEXTF_NONE        = 0,             OLECMDTEXTF_NAME        = 1,             OLECMDTEXTF_STATUS      = 2,     } OLECMDTEXTF;     typedef enum OLECMDEXECOPT {             OLECMDEXECOPT_DODEFAULT         = 0,             OLECMDEXECOPT_PROMPTUSER        = 1,             OLECMDEXECOPT_DONTPROMPTUSER    = 2,             OLECMDEXECOPT_SHOWHELP          = 3     } OLECMDEXECOPT;     HRESULT QueryStatus([in] LPGUID pguidCmdGroup,                         [in] ULONG cCmds,                         [in,out] LPOLECMD *prgCmds,                         [in,out] LPOLECMDTEXT *pCmdText);     HRESULT Exec([in] LPGUID pguidCmdGroup,                    [in] DWORD nCmdID,                  [in] DWORD nCmdExecOpt,                            [in] VARIANT *pvaIn,                            [in,out] VARIANT *pvaOut); } 
12.2.2.2 IServiceProvider

IServiceProvider contains one method (which is very much like QueryInterface ) called QueryService , as the following table shows:

Method

Description

QueryService

Creates or accesses the specified service and returns an interface pointer to it.

12.2.2.2.1 QueryService

This method is very similar to QueryInterface in form and function. It takes a pointer to the GUID of the service we are looking for, namely IWebBrowserApp , and a pointer to the interface we want from that service (in our case, IWebBrowser2 ). It returns the interface we have requested (hopefully). Its syntax is:

 HRESULT QueryService(REFGUID guidService, REFIID riid, void** ppvOut); 

Its parameters are as follows:

guidService

[in] Identifier for the requested service.

riid

[in] Identifier for the interface on the request service.

ppvOut

[out, retval] Address that receives the interface pointer requested by riid .

Example 12.11 contains the complete listing for IServiceProvider .

Example 12.11. IServiceProvider Interface
 [     uuid(6d5140c1-7436-11ce-8034-00aa006009fa),     helpstring("IServiceProvider Interface"),     odl ] interface IServiceProvider : IUnknown {     HRESULT QueryService([in]  LPGUID guidService,                           [in]  REFIID riid,                          [out, retval] IDispatch **ppvObject); } 

12.2.3 SetSite Revisited

As stated earlier, we are going to have to implement SetSite a little bit differently. Let's take the code piece by piece. The following code fragment should look familiar. It's the same code you would implement for a BHO:

 Private Sub IObjectWithSite_SetSite(_     ByVal pSite As VBShellLib.IUnknownVB)          If ObjPtr(pSite) = 0 Then         CopyMemory m_ie, 0&, 4         Exit Sub     End If          Set m_pUnkSite = pSite    'Save the site pointer for GetSite 

Here's where things get a little different. Instead of asking for IWebBrowser2 , we need to get IServiceProvider . So, we'll query the site pointer for this interface:

 Dim pServiceProvider As IServiceProvider Set pServiceProvider = m_pUnkSite 

Now, QueryService takes a pointer to a GUID for both of its arguments. To get these GUIDs, we need to declare a couple of constants in clsInetSpeak that represent the string values of the GUIDs we are interested in. They are as follows:

 Private Const IID_IWebBrowserApp = _      "{0002DF05-0000-0000-C000-000000000046}" Private Const IID_IWebBrowser2 = _      "{D30C1661-CDAF-11D0-8A3E-00C04FC9E26E}" 

We are going to use an API function found in ole32.dll to map these string values to an actual GUID. This function is called CLSIDFromString , and it is declared as follows:

 Public Declare Function CLSIDFromString Lib "ole32.dll" _      (ByVal lpszProgID As Long, pCLSID As GUID) As Long 

Its parameters are:

lpszProgId

This is a pointer to the string representation of a CLSID .

pCLSID

This is a pointer to a GUID structure defined as so:

 Public Type GUID     Data1 As Long     Data2 As Integer     Data3 As Integer     Data4(7) As Byte End Type 

Now, back to SetSite . Continuing on with our implementation, we declare two variables to hold the IIDs, clsidWebApp and clsidWebBrowser2 , and then we call CLSIDFromString to populate those structures:

 Dim clsidWebApp As GUID Dim clsidWebBrowser2 As GUID      CLSIDFromString StrPtr(IID_IWebBrowserApp), clsidWebApp CLSIDFromString StrPtr(IID_IWebBrowser2), clsidWebBrowser2 

Now all we have to do is call QueryService and release IServiceProvider . We have to use VarPtr , because QueryService needs the pointer to the GUID:

 Set m_ie = pServiceProvider.QueryService(     VarPtr(clsidWebApp), VarPtr(clsidWebBrowser2))          Set pServiceProvider = Nothing 

We now have our IWebBrowser2 interface. The good news here is that GetSite is the same, so we don't have to worry about it at all. The complete listing for SetSite can be found in Example 12.12.

Example 12.12. SetSite for Browser Extension
 Private Sub IObjectWithSite_SetSite(ByVal pSite As IUnknownVB)          If ObjPtr(pSite) = 0 Then         CopyMemory m_ie, 0&, 4         Exit Sub     End If          Set m_pUnkSite = pSite    'Save the site pointer for GetSite          Dim pServiceProvider As IServiceProvider     Set pServiceProvider = m_pUnkSite          Dim clsidWebApp As GUID     Dim clsidWebBrowser2 As GUID          'Query service provider to get IWebBrowser2 (Internet Explorer)     CLSIDFromString StrPtr(IID_IWebBrowserApp), clsidWebApp     CLSIDFromString StrPtr(IID_IWebBrowser2), clsidWebBrowser2          Set m_ie = pServiceProvider.QueryService(_         VarPtr(clsidWebApp), VarPtr(clsidWebBrowser2))              Set pServiceProvider = Nothing           End Sub 

12.2.4 Adding the Menu Item

To add a menu item for our component, we need to add several values to the registry. They all reside under the key shown in Figure 12.3. Take a look, then we'll talk about it.

Figure 12.3. Browser extension key
figs/vshl.1203.gif

All values for our browser extension will go under the HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Extension\< GUID > key. This is important. The GUID denoted by the x es in Figure 12.3 is not the CLSID of our component. It is just a randomly generated GUID that uniquely identifies this key. This value exists nowhere else in the registry. So, use GUIDGEN to create this key.

Once you have added this key to the registry, we need to add several values, which are shown in Figure 12.4.

Figure 12.4. Browser extension menu values
figs/vshl.1204.gif

The CLSID {1FBA04EE-3024-11d2-8F1F-0000F87ABD16} is a fixed value. Every browser extension you write needs that exact clsid value; it's not some made up value. If you search the registry for the value, you will find the entry, "Toolbar Extension for Executable."

ClsidExtension , however, is the registry value that points to our browser extension. MenuStatusBar contains the text that will be displayed in the status bar when our menu item is selected, and MenuText contains the text of our menu item.

You cannot use an ampersand ( & ) to define hotkeys for this menu because there is nothing in place to prevent conflicts with other menu items.

Browser extension commands are located under the Tools menu in Internet Explorer 5.0. They are also available from Explorer, but only when browsing the Web.

Finally, there is another optional value you can add called MenuCustomize . If you create a string value with this name and set it equal to "help" (case doesn't matter), your menu item will appear under the Help menu instead of the Tools menu. If the MenuCustomize key does not exist or its value is set to anything other than "help," the menu item will appear under the Tools menu.

12.2.5 Adding the Toolbar Button

Adding a toolbar button is as simple as adding three more string values to the registry and creating a couple of icons. See Figure 12.5 for the additional registry values. You must create four icons for the button. These include a 20 figs/u00d7.gif 20 icon and a 16 figs/u00d7.gif 16 icon that represent the default state of the button, and two more icons of the same dimensions that represent the "hot" image that is displayed when the mouse is over the button.

Figure 12.5. Registry settings for a toolbar button
figs/vshl.1205.gif

The icon values in the registry can point to an .ico file or, as you can see in Figure 12.5, a resource in a Windows executable. The format for the latter option is:

   Executable   ,   Resource ID   

If you opt to store the icons in a DLL, you should probably register your components in the system directory. This will free you from having to map a path for these entries.

After you have added these entries, you will need to select View figs/u2192.gif Toolbars figs/u2192.gif Customize from Internet Explorer's main menu. A dialog will present you with the opportunity to add the button to the toolbar.

12.2.6 QueryStatus

The shell calls IOleCommandTarget::QueryStatus to inquire about the status of a menu or toolbar command (i.e., whether the command is enabled or disabled). The funny thing is, if we do not supply the status information, Explorer will disable any toolbar buttons we may have added (this does not affect menu items) after we use them for the first time. So, to keep things working properly, we will need to implement this method.

Actually, this behavior is interesting because after our browser extension has been called once, QueryStatus will be called every time any command is issued. It will also be called when the user just clicks on a web page! This means you can write code to enable or disable your extension based on existing Explorer commands like Refresh, Home, or Print.

QueryStatus is defined as follows:

 HRESULT QueryStatus( const GUID *pguidCmdGroup, ULONG cCmds,      OLECMD *prgCmds, OLECMDTEXT *pCmdText); 

The first parameter, pguidCmdGroup , is a pointer to the GUID that represents the command group . In terms of our extension, we do not care about this value; it is going to be NULL .

The second parameter, cCmds , is the count of OLECMD structures in the array pointed to by the third parameter, prgCmds . These two parameters are the ones that concern us most. Our implementation of QueryStatus will loop through these commands and merely tell the shell that the commands are supported and enabled.

The last parameter, pCmdText , is a pointer to an OLECMDTEXT structure that is used to return command name and status information back to the shell. We do not need to worry about this parameter for browser extensions.

The code for QueryStatus is shown in Example 12.13.

Example 12.13. QueryStatus
 Private Sub IOleCommandTarget_QueryStatus( _      ByVal pguidCmdGroup As VBShellLib.LPGUID, _      ByVal cCmds As VBShellLib.ULONG, _      ByVal prgCmds As VBShellLib.LPOLECMD, _      ByVal pCmdText As VBShellLib.LPOLECMDTEXT)     Dim i As Integer               For i = 0 To cCmds - 1         Dim cmd As OLECMD                      CopyMemory cmd, ByVal prgCmds + (Len(cmd) * i), Len(cmd)         cmd.cmdf = OLECMDF_SUPPORTED Or OLECMDF_ENABLED         CopyMemory ByVal prgCmds + (Len(cmd) * i), cmd, Len(cmd)          Next i      End Sub 

In the case of our example, when QueryStatus is called, cCmds will always be 1, since our extension only adds a single command to the Explorer menu. Regardless, the method has been coded to demonstrate how you would handle more than one command. Since prgCmds is a pointer to an array, pointer arithmetic is used to calculate the offset of the command structure in memory. A local copy of the structure is created, and the appropriate values are added to the structure. The local instance of the command is then copied back into the array.

12.2.7 Exec

Yes, it's time to actually accomplish something with this component. The extension in this chapter performs an important service. It translates Internet-speak into plain English. Remember the first time you got on the Web and saw a sentence like this: "Well, IMHO, I think this book rocks!" IMHO? What the heck is IMHO? Well, with our handy translator installed, you will be able to highlight these bizarre acronyms and watch as IMHO is translated into, "In my honest/ humble opinion," right on the page. That's sort of practical, right?

The code to accomplish this amazing feat goes into IOleCommandTarget::Exec , which is shown in Example 12.14. Once we get the IHTMLDocument2 interface, its Selection property will give us the text that is selected on the current page in the form of an IHTMLSelectionObject interface pointer. A method of this interface gives us the text range of the current selection, and from there we are able to get to the text itself. Then we are free to modify it in any way we see fit. The translation is accomplished with a humongous Select...Case block. If any of your favorite goofy acronyms have been left out, go ahead and add them.

Example 12.14. Exec Implementation
 Private Sub IOleCommandTarget_Exec(_      ByVal pguidCmdGroup As VBShellLib.LPGUID, _      ByVal nCmdID As VBShellLib.DWORD, _      ByVal nCmdExecOpt As VBShellLib.DWORD, _      pvaIn As Variant, _      pvaOut As Variant)         Translate End Sub Private Sub Translate(  )     Dim pDoc As IHTMLDocument2     Dim pTextRange As IHTMLTxtRange     Dim sDef As String          Set pDoc = m_ie.Document     Set pTextRange = pDoc.selection.createRange     Select Case Trim(pTextRange.Text)         Case "IMHO"             sDef = "[In My Honest/Humble Opinion]"         Case "FYI"             sDef = "[For Your Information]"         Case "AFAIK"             sDef = "[As Far As I Know]"         Case "LOL"             sDef = "[Laughing Out Loud/Lots Of Laughs]"         Case "BTW"             sDef = "[By The Way]"         Case "TIA"             sDef = "[Thanks In Advance]"         Case "RTM"             sDef = "Read The Manual"         Case "RTFM"             sDef = "Read The Fine Manual"         Case ":)", ":-)"             sDef = "[Smile]"         Case ";)", ";-)"             sDef = "[Wink]"         Case ":(", ":-("             sDef = "[Boo-hoo]"         Case Else             sDef = pTextRange.Text     End Select     pTextRange.Text = sDef     Set pTextRange = Nothing     Set pDoc = Nothing End Sub 

If you followed along with the chapter, the browser extension should already have the appropriate entries in the registry. But you will need to move the extension into your system directory if you want the icons for the toolbar button to be displayed properly. After you move the extension, register it using regsvr32.exe . Also, don't forget that in order for the toolbar button to show up, you will have to add it to the toolbar yourself by selecting View figs/u2192.gif Toolbars figs/u2192.gif Customize from IE 5.0's menu.

only for RuBoard - do not distribute or recompile


Visual Basic Shell Programming
Visual Basic Shell Programming
ISBN: B00007FY99
EAN: N/A
Year: 2000
Pages: 128

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net