14.3 The Project

only for RuBoard - do not distribute or recompile

14.3 The Project

The project for this chapter is fairly straightforward. We are going to start with the browser helper object that we built in Chapter 12 and give it a user interface. Not too creative, but that's not really the point. The point is to show you how you can take a simple component like a BHO and give it a user interface. Coming up with a creative way to use this knowledge will be your job. Now, how's that for passing the buck? You might be saying to yourself, "The component in Chapter 12 already has a user interface." True, but browser extensions (as opposed to BHOs) are for IE 5.0 and up. A BHO with a docking window is backwards compatible to IE 4.0.

14.3.1 Primary Component

Let's start with the IObjectWithSite::SetSite method of the component we built in Example 12.2. We'll add the code necessary for the docking window and then discuss the details. We only need to add a few new lines of code, which appear in boldface in Example 14.1.

Example 14.1. SetSite in Primary Object
 Private Const IID_IShellBrowser = _      "{000214e2-0000-0000-c000-000000000046}" Private Const IID_IDockingWindowFrame = _     "{47d2657a-7b27-11d0-8ca9-00a0c92dbfe8}" Private szToolbar As String Private Sub Class_Initialize(  )     szToolbar = "IEDockingWindow" End Sub 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  Dim clsidShellBrowser As GUID     Dim clsidDockWndFrame As GUID  'Query service provider to get IWebBrowser2 (InternetExplorer)     CLSIDFromString StrPtr(IID_IWebBrowserApp), clsidWebApp     CLSIDFromString StrPtr(IID_IWebBrowser2), clsidWebBrowser2  CLSIDFromString StrPtr(IID_IShellBrowser), _           clsidShellBrowser     CLSIDFromString StrPtr(IID_IDockingWindowFrame), _           clsidDockWndFrame  Set m_ie = pServiceProvider.QueryService(_         VarPtr(clsidWebApp), VarPtr(clsidWebBrowser2))  Set m_pDockingWindowFrame = _              pServiceProvider.QueryService _                           (VarPtr(clsidShellBrowser), _                           VarPtr(clsidDockWndFrame))               Dim pDockWnd As clsDockWindow     Set pDockWnd = New clsDockWindow     pDockWnd.Initialize DW_BOTTOM          Set pDockWnd.InternetExplorer = m_ie     Set pDockWnd.InetSpeak = Me     m_pDockingWindowFrame.AddToolbar pDockWnd, _                                   ByVal StrPtr(szToolbar), _                                   0  Set pServiceProvider = Nothing          End Sub 

The first thing that is added are two new constants that contain the string representations of the CLSIDs for IShellBrowser and IDockingWindowFrame . We will use them in conjunction with the CLSIDFromString API to obtain their actual numerical equivalents. After we have the CLSIDs, we can then query the "Shell Browser Service" to obtain a pointer to IDockingWindowFrame .

If you have been wondering what other "services" are provided by the shell through IServiceProvider , you are not alone. This information does not seem to exist in the Platform SDK.

Now that we have access to IDockingWindowFrame , we can create an instance of clsDockWindow. This is our class that will implement all the specifics of the docking window; it implements IObjectWithSite and IDockingWindow . But before we pass the object to AddToolbar , we'll need to do some initialization work first. This includes telling the docking window where it will be displayed and providing it with references to Internet Explorer and our InetSpeak object.

The first thing we do is call the Initialize method of clsDockWindow (not to be confused with Class_Initialize, which is, of course, called automatically). This is a method of the class itself, created for our own convenience. It takes a location parameter; either DW_TOP , DW_BOTTOM , DW_LEFT , or DW_RIGHT . The docking window will use this information in order to position itself in the proper location within Explorer's frame window. Basically, this function sets a Private member variable in clsDockWindow. The IDockingWindow::ResizeBorderDW will make use of this parameter when it requests border space for the window. Ignore the implementation details of Initialize for now; we'll look at the actual method in the next section.

Next, we pass the docking window a reference to Internet Explorer and to clsInetSpeak, our BHO. This gives the docking window access to all those Internet goodies as well as access back to the primary component, which it will need to execute commands.

Finally, we call IDockingWindowFrame::AddToolbar to display the docking window. At this point the shell calls IObjectWithSite::SetSite on the docking window, transferring control away from the primary component.

14.3.2 Docking Window Component

The first thing we need to do in order to create our docking window component is to add a new class module to the project called clsDockWindow and make sure that it is implementing the appropriate interfaces:

 Implements IDockingWindow Implements IObjectWithSite 

Before we start implementing any interfaces, let's get our class methods out of the way. If you remember from Example 14.1, we have three such methods : Initialize , InternetExplorer , and InetSpeak .

The first method, Initialize , which we've already discussed in the previous section, is just used to pass position and size information to the docking window. This information will be used when the docking window negotiates its border space within Explorer's client area. This function is simply:

 Public Enum DW_LOCATION     DW_TOP = 1     DW_LEFT = 2     DW_BOTTOM = 3     DW_RIGHT = 4 End Enum Private m_location As DW_LOCATION Private m_nSize As Integer Public Sub Initialize(ByVal Location As DW_LOCATION)     m_location = Location     m_nSize = size End Sub 

The class is also going to need a subroutine that the docking window form can call in order to specify its dimensions. That function looks like so:

 Private m_nWidth As Integer Private m_nHeight As Integer Friend Sub SetSize(ByVal x As Integer, ByVal y As Integer)     m_nWidth = x     m_nHeight = y End Sub 

The next two functions are used to pass object references to the docking window. These two functions are self-explanatory and are defined like so:

 Public Property Set InternetExplorer(ie As InternetExplorer)     Set m_ie = ie     Set frmDock.InternetExplorer = m_ie End Property Public Property Set InetSpeak(ins As clsInetSpeak)     Set m_iis = ins     Set frmDock.InetSpeak = m_iis End Property 

As you can see, these properties are also mirrored within the docking window form. This will give the form access to these objects as well.

With the exception of the InetSpeak property, clsDockWindow is generic. There are no other specific references used in the class. Should you want to reuse the class, you could merely replace the InetSpeak property with one that accepts objects of your own type. SetSite

Now, the easiest thing to do is get the IObjectWithSite implementation out of the way. The docking window's version of SetSite is much simpler than our other implementations . All we need to do is query the site pointer for the IDockingWindowSite interface. We'll cache it away in a private member variable. Later, the docking window will use it to negotiate its border space.

Another thing we need to be aware of is that SetSite will be called again when Explorer shuts down. This is no different than any other time we have implemented IObjectWithSite , so just consider this a reminder. It's easy enough to determine whether Explorer has been shut down. All we need to do is examine the cached site pointer. If its address is not zero, then we know SetSite has already been called. If that's the case, we'll need to exit. Example 14.2 contains the details.

Example 14.2. The Docking Window's SetSite Method
 Private m_pDockSite As IDockingWindowSite Private Sub IObjectWithSite_SetSite( _      ByVal pSite As VBShellLib.IUnknownVB)     If (ObjPtr(m_pDockSite)) Then         Set m_pDockSite = Nothing         Exit Sub     End If     If ObjPtr(pSite) Then         Set m_pDockSite = pSite     End If End Sub GetSite

There is nothing new to GetSite . It is implemented like we have always done before:

 Private Sub IObjectWithSite_GetSite( _              ByVal priid As VBShellLib.REFIID, _              ppvObj As VBShellLib.VOID)     Dim pUnknown As IUnknownVB     Set pUnknown = m_pDockSite     pUnknown.QueryInterface priid, ppvObj   End Sub The docking window form

Now before we continue, let's actually design the docking window. We need to add a new form to the project called frmDock . As with all docking windows you write, it needs to be borderless. Also, you want to size the form approximately to the size you want it be when it is displayed. The band has a height of 600. 600/15 = 40, which is the height in pixels that we want for display.

After adding the form, you can add a command button called cmdInetSpeak to the form. This button will carry out our command. The band is shown in Figure 14.2.

Figure 14.2. Docking window design

We need to add a few properties to the docking window that will allow the component to pass references into the form for IWebBrowser2 , clsInetSpeak , and IDockingWindow . These properties look like this:

 Private m_dw As clsDockWindow Private WithEvents m_ie As InternetExplorer Private m_iis As clsInetSpeak Friend Property Set InternetExplorer(ie As InternetExplorer)     Set m_ie = ie End Property Friend Property Set InetSpeak(iis As clsInetSpeak)     Set m_iis = iis End Property Friend Property Set DockingWindow(dw As clsDockWindow)     Set m_dw = dw End Property 

Now that the form has a valid reference to clsInetSpeak, we can add code to the command button that actually fires the command. This is simply:

 Private Sub cmdInetSpeak_Click(  )     m_iis.Translate End Sub 

Also, this is an Internet-specific component. It doesn't really apply to file objects. So we need a way to disable the browser when we are not browsing the Web. We can capture the BeforeNavigate2 event from our IE reference to accomplish the task. The code looks like this:

 Private Sub m_ie_BeforeNavigate2(ByVal pDisp As Object, _                                   URL As Variant, _                                   Flags As Variant, _                                   TargetFrameName As Variant, _                                   PostData As Variant, _                                   Headers As Variant, _                                   Cancel As Boolean)     'Need to make sure command does not get      'executed against non-HTML     If InStr(URL, "http") Then         cmdInetSpeak.Enabled = True     Else         cmdInetSpeak.Enabled = False     End If      End Sub 

Last but not least, we need to add some code to the Form_Load and Form_Unload events. When the form is loaded, its size information becomes valid. We'll use this opportunity to inform the docking window class about the form's size:

 Private Sub Form_Load(  )     m_dw.SetSize Me.Width / Screen.TwipsPerPixelX, _                  Me.Height / Screen.TwipsPerPixelY End Sub 

The Form_Unload event will merely be used to clean up after ourselves ; we'll free up our object references here:

 Private Sub Form_Unload(Cancel As Integer)     Set m_dw = Nothing     Set m_ie = Nothing     Set m_iis = Nothing End Sub 

Okay, we have the docking window designed, so let's get back to clsDockWindow and our IDockingWindow implementation. The big action happens in IDockingWindow::ResizeBorderDW and IDockingWindow::ShowDW , so we'll save those methods for last. For now, we'll get the easier methods out of the way. CloseDW

Some things never change. Just as was the case with band objects, this method's only responsibility is to unload the docking window:

 Private Sub IDockingWindow_CloseDW(ByVal dwReserved As Long)     Unload frmDock End Sub GetWindow

This method also performs the same duty that it did when we were discussing band objects. All it needs to do is return with a window handle for our docking window:

 Private Function IDockingWindow_GetWindow(  ) As Long     IDockingWindow_GetWindow = frmDock.hwnd End Function ResizeBorderDW

Okay, now we're ready to get down to business. We need to implement ResizeBorderDW , which we have not done before. All we had to do for bands was capture the Resize event and perform any resizing that might be necessary. The container was able to handle itself as far as size and position. This is not the case here. Whenever Explorer is resized, the docking window has to renegotiate its border space and resize itself accordingly . Let's take a peek at the implementation for this method (see Example 14.3).

Example 14.3. ResizeBorderDW Implementation
 Private Sub IDockingWindow_ResizeBorderDW( _       ByVal prcBorder As Long, _       ByVal punkToolbarSite As VBShellLib.IUnknownVB, _       ByVal fReserved As Boolean)          If NegotiateBorderSpace Then         IDockingWindow_ShowDW True     End If      End Sub 

Well, that looks innocuous , doesn't it? That's because all the work is being done by a private helper function called NegotiateBorderSpace . If the border space is obtained successfully, the function then calls ShowDW to display the docking window. We'll talk about ShowDW soon, but now, let's get into NegotiateBorderSpace , which is shown in Example 14.4.

Example 14.4. NegotiateBorderSpace
 Private m_rcDisplay As RECT Private Function NegotiateBorderSpace(  ) As Boolean     NegotiateBorderSpace = False          Dim pDockingWindow As IDockingWindow     Dim bw As RECT  'BORDERWIDTHS     Dim rcBorder As RECT     Dim rcTemp As RECT     Dim nSize As Integer          Select Case m_location         Case DW_TOP             nSize = m_nHeight             bw.Top = nSize         Case DW_LEFT             nSize = m_nWidth             bw.Left = nSize         Case DW_BOTTOM             nSize = m_nHeight             bw.bottom = nSize         Case DW_RIGHT             nSize = m_nWidth             bw.Right = nSize     End Select          Set pDockingWindow = Me     m_pDockSite.RequestBorderSpaceDW pDockingWindow, _          ByVal VarPtr(bw)     m_pDockSite.SetBorderSpaceDW pDockingWindow, _          ByVal VarPtr(bw)          m_pDockSite.GetBorderDW pDockingWindow, ByVal VarPtr(rcBorder)              Select Case m_location         Case DW_RIGHT             m_rcDisplay.Left = rcBorder.Right - m_nSize             m_rcDisplay.Top = rcBorder.Top             m_rcDisplay.Right = rcBorder.Right             'Accomodate Status Bar             m_rcDisplay.bottom = rcBorder.bottom - 82              Case DW_LEFT             m_rcDisplay.Left = rcBorder.Left             m_rcDisplay.Top = rcBorder.Top             m_rcDisplay.Right = rcBorder.Left + m_nSize             'Accomodate Status Bar             m_rcDisplay.bottom = rcBorder.bottom - 82              Case DW_TOP             m_rcDisplay.Left = rcBorder.Left             m_rcDisplay.Top = rcBorder.Top             m_rcDisplay.Right = rcBorder.Right - rcBorder.Left             m_rcDisplay.bottom = m_nSize         Case DW_BOTTOM             m_rcDisplay.Left = rcBorder.Left             m_rcDisplay.Top = rcBorder.bottom - m_nSize             m_rcDisplay.Right = rcBorder.Right - rcBorder.Left             ' -1 leaves beveled edge on Status Bar             m_rcDisplay.bottom = m_nSize - 1                  End Select          NegotiateBorderSpace = True End Function 

Let's step through this nice and easy; it's actually much easier than it looks. The first thing that happens is that a RECT structure, bw , is filled out based on the parameters we passed to the Initialize function of the class. In terms of the chapter example, we asked for a docking window on the bottom of Explorer's frame window. Earlier, when our window was loaded, its size (converted from twips to pixels) was passed back to clsDockWindow . Since the form's height is 600, and 600 divided by Screen.TwipsPerPixelY (15) equals 40, we will end up with a RECT structure whose bottom member is set to 40. All the other values of the RECT will contain 0.

Next, we get a reference to our own IDockingWindow interface pointer. This is passed to IDockingWindowSite::RequestBorderSpaceDW along with a pointer to bw . Translation: allocate border space that is 40 pixels tall at the bottom of your client area. This object is going to need it.

Next, we finalize the request by calling SetBorderSpaceDW . This actually allocates and reserves the space for our docking window.

Now, things get a little confusing. We call GetBorderDW , which returns to us another RECT structure, rcBorder , that supposedly contains the dimensions of the space that has been allocated for our docking window. In practice, the method seems to return the dimensions of the entire client area. So the rest of this function is dedicated to determining the actual location of the docking window based on the size of the client area and the location and size that we have asked for. When all is said and done, we have a private member variable, m_rcDisplay , that contains the actual coordinates of the window. ShowDW

The last thing on our list for clsDockWindow is ShowDW . As you might remember from Chapter 13, Thi method is called when the window is about to be either displayed or destroyed . Example 14.5 contains the code listing.

Example 14.5. ShowDW
 Private m_hwnd As hwnd Private Sub IDockingWindow_ShowDW(ByVal fShow As Boolean)          Dim pDockingWindow As IDockingWindow     Dim rcBorderWidths As RECT     Dim dwStyle As DWORD          If Not m_hwnd Then         m_hwnd = frmDock.hwnd         dwStyle = GetWindowLong(m_hwnd, GWL_STYLE)         dwStyle = dwStyle Or WS_CHILD Or WS_CLIPSIBLINGS         SetWindowLong m_hwnd, GWL_STYLE, dwStyle         SetParent frmDock.hwnd, m_pDockSite.GetWindow     End If          If (fShow) Then              ShowWindow frmDock.hwnd, SW_SHOW         MoveWindow frmDock.hwnd, m_rcDisplay.Left, _                     m_rcDisplay.Top, m_rcDisplay.Right, _                     m_rcDisplay.bottom, True          Else              ShowWindow frmDock.hwnd, SW_HIDE         Set pDockingWindow = Me                  'Release border space - rcBorderWidths is empty         m_pDockSite.SetBorderSpaceDW pDockingWindow, _          ByVal VarPtr(rcBorderWidths)              End If      End Sub 

The docking window, like the band object, also needs to have some style bits changed and have its parent window set to the container window. Remember, we do this so that when the docking window gets the focus, Explorer will not lose the focus. The idea here is that the docking window is fully integrated, right? But since ShowDW will be called many times throughout the docking window's life, we need to make sure this does not happen more than once.

If fShow is True , we simply show the window and move its position to coordinates specified by the RECT m_rcDisplay (the coordinates were determined in NegotiateBorderSpace ).

If fShow is False , we hide the window and release the border space. To release the border space, IDockingWindowSite::SetBorderSpaceDW is called with the address of an empty RECT .

That's it; we have done everything we need in order to implement a docking window.

only for RuBoard - do not distribute or recompile

Visual Basic Shell Programming
Visual Basic Shell Programming
ISBN: B00007FY99
Year: 2000
Pages: 128

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