Custom iDocHostUIHandler interface for webgadgets

Share your advanced PureBasic knowledge/code with the community.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Custom iDocHostUIHandler interface for webgadgets

Post by srod »

Hi,

in response to a direct question from a forum member to explain how to prevent the default web-gadget context menus from being shown, I quickly hacked up the following code which does exactly this. It is based on some old code of Freak's I had lying around.

From MSDN :
MSHTML implements IDocHostUIHandler to communicate with the host about its user interface status. The host can also implement this interface to extend, replace, or disable the default UI elements, such as menus, context menus, and toolbars. When replacing or disabling elements, the host merely returns S_OK, S_FALSE, or E_NOTIMPL from most methods. When extending the user interface, the host should delegate as necessary to the default MSHTML implementation.
All the code does then is implement it's own iDocHostUIHandler interface which it uses to supplement the default one provided by the webgadget. It does not replace the default implementation outright (unlike most handlers I see). This means you can pick and choose which elements of the available functionality you wish to supplement etc.

In the demo code provided I simply use the custom interface to prevent the context menus from being shown. You can of course choose to show your own menus instead based upon the object beneath the cursor and so on.

Code: Select all

;/////////////////////////////////////////////////////////////////////////////////
;An example of setting a custom iDocHostUIHandler interface for a web-gadget.
;This interface allows you to replace the menus, toolbars and context menus etc.
;We use it to show how to disable a webgadget's context menus.
;
;Stephen Rodriguez (srod). Based upon some code written by Freak.
;/////////////////////////////////////////////////////////////////////////////////

;/////////////////////////////////////////////////////////////////////////////////
;Most custom implementations of the iDocHostUIHandler interface simply replace the default
;implementation out-right. This implementation does not and instead falls back to the default implementation
;for those methods we are not interested in. Amongst other things this prevents certain memory leaks (apparently!)
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
Structure _IDocHostUIHandler
  *vTable.i
  ref.i
  iDocHostUiHandler.iDocHostUiHandler
EndStructure
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;The following function sets the specified webgadget's iDocHostUIHandler interface to a custom implementation which we must provide ourselves.
;Return a HRESULT value (#S_OK if successful).
Procedure.i SetCustomDocHostUIHandler(id, vTableAddress)
  Protected result=#E_FAIL, hWnd, iBrowser.IWebBrowser2, iDispatch.IDispatch, iDocument.IHTMLDocument2, iOLE.IOleObject, iDocHostUIHandler.IDocHostUIHandler
  Protected iCustomDoc.ICustomDoc, iOLEClientSite.IOleClientSite, *this._IDocHostUIHandler
  hWnd = GadgetID(id)
  If hWnd
    iBrowser = GetWindowLong_(hWnd, #GWL_USERDATA)
    If iBrowser
      If iBrowser\get_Document(@iDispatch) = #S_OK
        If iDispatch\QueryInterface(?IID_IHTMLDocument2, @iDocument) = #S_OK
          If iDocument\QueryInterface(?IID_IOleObject, @iOLE) = #S_OK
            If iOLE\GetClientSite(@iOLEClientSite) = #S_OK
              If iOLEClientSite\QueryInterface(?IID_IDocHostUIHandler, @iDocHostUIHandler) = #S_OK
                If iDocument\QueryInterface(?IID_ICustomDoc, @iCustomDoc) = #S_OK
                  *this = AllocateMemory(SizeOf(_IDocHostUIHandler))
                  If *this
                    *this\vTable = vTableAddress
                    *this\iDocHostUiHandler = iDocHostUIHandler
                    iCustomDoc\SetUIHandler(*this)
                    result = #S_OK
                  Else
                    iDocHostUIHandler\Release() 
                  EndIf            
                  iCustomDoc\Release()
                Else
                  iDocHostUIHandler\Release()
                EndIf
              EndIf
              IOleClientSite\Release()
            EndIf
            iOLE\Release()
          EndIf
          iDocument\Release()
        EndIf
        iDispatch\Release()
      EndIf
    EndIf
  EndIf
  ProcedureReturn result
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////


;-TEST CODE.

;/////////////////////////////////////////////////////////////////////////////////
If OpenWindow(0, 0, 0, 600, 600, "WebGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  WebGadget(0, 10, 10, 600, 600, "http://www.google.com")
  ;Here we set our custom iDocHostUIHandler interface. Below this code is our custom implementation for this interface.
  ;We prevent the context menus from being displayed by simply returning #S_OK from the ShowContextMenu() method.
  ;All other methods (except the iUnknown ones) simply call the default iDocHostUIHandler methods.
    SetCustomDocHostUIHandler(0, ?IDocHostUIHandlerFunctionTable)

  Repeat 
    Event = WaitWindowEvent();
    Select Event
    Case #PB_Event_CloseWindow
      Break
    EndSelect
  ForEver
EndIf

End
;/////////////////////////////////////////////////////////////////////////////////


;-CUSTOM iDocHostUIHandler IMPLEMENTATION.

;/////////////////////////////////////////////////////////////////////////////////
;iUnknown.
Procedure.i IDocHostUIHandler_QueryInterface(*this._IDocHostUIHandler, riid, *ppObj.INTEGER)
  Protected hResult = #E_NOINTERFACE, iunk.iUnknown
  If *ppObj And riid 
    *ppObj\i = 0
    If CompareMemory(riid, ?IID_IUnknown, SizeOf(IID)) Or CompareMemory(riid, ?IID_IDocHostUIHandler, SizeOf(IID))
      *ppObj\i = *this
      *this\ref+1
      hResult = #S_OK
    EndIf 
  EndIf
  ProcedureReturn hResult
EndProcedure


;iUnknown.
Procedure.i IDocHostUIHandler_AddRef(*this._IDocHostUIHandler)
  *this\ref = *this\ref + 1
  ProcedureReturn *this\ref
EndProcedure


;iUnknown.
Procedure.i IDocHostUIHandler_Release(*this._IDocHostUIHandler)
  Protected refCount
  *this\ref = *this\ref - 1
  refCount = *this\ref
  If *this\ref = 0
    *this\iDocHostUiHandler\Release()
    FreeMemory(*this) 
  EndIf
  ProcedureReturn refCount
EndProcedure


;Return #S_FALSE to show the default context menus.
Procedure.i IDocHostUIHandler_ShowContextMenu(*this._IDocHostUIHandler, dwID, ppt, pcmdTarget, pdispReserved)
  Debug "Context menu prohibited!"
  ProcedureReturn #S_OK
EndProcedure


Procedure.i IDocHostUIHandler_GetHostInfo(*this._IDocHostUIHandler, *pInfo)
  ProcedureReturn *this\iDocHostUiHandler\GetHostInfo(*pInfo)
EndProcedure


Procedure.i IDocHostUIHandler_ShowUI(*this._IDocHostUIHandler, dwID, pActiveObject, pCommandTarget, pFrame, pDoc)
  ProcedureReturn *this\iDocHostUiHandler\ShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc)
EndProcedure


Procedure.i IDocHostUIHandler_HideUI(*this._IDocHostUIHandler)
  ProcedureReturn *this\iDocHostUiHandler\HideUI()
EndProcedure

Procedure.i IDocHostUIHandler_UpdateUI(*this._IDocHostUIHandler)
  ProcedureReturn *this\iDocHostUiHandler\UpdateUI()
EndProcedure

Procedure.i IDocHostUIHandler_EnableModeless(*this._IDocHostUIHandler, fEnable)
  ProcedureReturn *this\iDocHostUiHandler\EnableModeless(fEnable)
EndProcedure

Procedure.i IDocHostUIHandler_OnDocWindowActivate(*this._IDocHostUIHandler, fActivate)
  ProcedureReturn *this\iDocHostUiHandler\OnDocWindowActivate(fActivate)
EndProcedure

Procedure.i IDocHostUIHandler_OnFrameWindowActivate(*this._IDocHostUIHandler, fActivate)
  ProcedureReturn *this\iDocHostUiHandler\OnFrameWindowActivate(fActivate)
EndProcedure

Procedure.i IDocHostUIHandler_ResizeBorder(*this._IDocHostUIHandler, prcBorder, pUIWindow, fFrameWindow)
  ProcedureReturn *this\iDocHostUiHandler\ResizeBorder(prcBorder, pUIWindow, fFrameWindow)
EndProcedure

Procedure.i IDocHostUIHandler_TranslateAccelerator(*this._IDocHostUIHandler, lpMsg, pguidCmdGroup, nCmdID)
  ProcedureReturn *this\iDocHostUiHandler\TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID)
EndProcedure

Procedure.i IDocHostUIHandler_GetOptionKeyPath(*this._IDocHostUIHandler, pchKey, dw)
  ProcedureReturn *this\iDocHostUiHandler\GetOptionKeyPath(pchKey, dw)
EndProcedure

Procedure.i IDocHostUIHandler_GetDropTarget(*this._IDocHostUIHandler, pDropTarget, ppDropTarget)
  ProcedureReturn *this\iDocHostUiHandler\GetDropTarget(pDropTarget, ppDropTarget)
EndProcedure

Procedure.i IDocHostUIHandler_GetExternal(*this._IDocHostUIHandler, ppDispatch.i)
   ProcedureReturn *this\iDocHostUiHandler\GetExternal(ppDispatch.i)
EndProcedure

Procedure.i IDocHostUIHandler_TranslateUrl(*this._IDocHostUIHandler, dwTranslate, pchURLIn, ppchURLOut)
  ProcedureReturn *this\iDocHostUiHandler\TranslateUrl(dwTranslate, pchURLIn, ppchURLOut)
EndProcedure

Procedure.i IDocHostUIHandler_FilterDataObject(*this._IDocHostUIHandler, pDO, ppDORet)
   ProcedureReturn *this\iDocHostUiHandler\FilterDataObject(pDO, ppDORet)
EndProcedure

DataSection

IID_IUnknown: ; 00000000-0000-0000-C000-000000000046
  Data.l $00000000
  Data.w $0000, $0000
  Data.b $C0, $00, $00, $00, $00, $00, $00, $46

IID_IHTMLDocument2: ; 332C4425-26CB-11D0-B483-00C04FD90119
  Data.l $332C4425
  Data.w $26CB, $11D0
  Data.b $B4, $83, $00, $C0, $4F, $D9, $01, $19

IID_IOleObject: ; 00000112-0000-0000-C000-000000000046
  Data.l $00000112
  Data.w $0000, $0000
  Data.b $C0, $00, $00, $00, $00, $00, $00, $46

IID_IDocHostUIHandler: ; BD3F23C0-D43E-11CF-893B-00AA00BDCE1A
  Data.l $BD3F23C0
  Data.w $D43E, $11CF
  Data.b $89, $3B, $00, $AA, $00, $BD, $CE, $1A

IID_ICustomDoc: ; 3050F3F0-98B5-11CF-BB82-00AA00BDCE0B
  Data.l $3050F3F0
  Data.w $98B5, $11CF
  Data.b $BB, $82, $00, $AA, $00, $BD, $CE, $0B

IDocHostUIHandlerFunctionTable:
  Data.i @IDocHostUIHandler_QueryInterface()
  Data.i @IDocHostUIHandler_AddRef()
  Data.i @IDocHostUIHandler_Release()
  Data.i @IDocHostUIHandler_ShowContextMenu()
  Data.i @IDocHostUIHandler_GetHostInfo()
  Data.i @IDocHostUIHandler_ShowUI()
  Data.i @IDocHostUIHandler_HideUI()
  Data.i @IDocHostUIHandler_UpdateUI()
  Data.i @IDocHostUIHandler_EnableModeless()
  Data.i @IDocHostUIHandler_OnDocWindowActivate()
  Data.i @IDocHostUIHandler_OnFrameWindowActivate()
  Data.i @IDocHostUIHandler_ResizeBorder()
  Data.i @IDocHostUIHandler_TranslateAccelerator()
  Data.i @IDocHostUIHandler_GetOptionKeyPath()
  Data.i @IDocHostUIHandler_GetDropTarget()
  Data.i @IDocHostUIHandler_GetExternal()
  Data.i @IDocHostUIHandler_TranslateUrl()
  Data.i @IDocHostUIHandler_FilterDataObject()
EndDataSection
;/////////////////////////////////////////////////////////////////////////////////
I may look like a mule, but I'm not a complete ass.
User avatar
luis
Addict
Addict
Posts: 3895
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Custom iDocHostUIHandler interface for webgadgets

Post by luis »

Nice, thank you !
"Have you tried turning it off and on again ?"
User avatar
ar-s
Enthusiast
Enthusiast
Posts: 344
Joined: Sat Oct 06, 2007 11:20 pm
Location: France

Re: Custom iDocHostUIHandler interface for webgadgets

Post by ar-s »

Thanks, that could be usefull
~Ar-S~
My Image Hoster for PB users
My webSite (french) with PB apps : LDVMULTIMEDIA
PB - 3.x / 5.7x / 6 - W11 x64 - Ryzen 7 3700x / #Rpi4

Code: Select all

r3p347 : 7ry : un71l d0n3 = 1
Post Reply