Q
I use the Active Accessibility SDK to obtain an IHTMLDoc-ument2 pointer from a window handle (HWND). Is there any way to obtain an IWebBrowser2 pointer from the IHTMLDocument2 pointer? I've tried QueryInterface on both the document pointer as well as the IHTMLWindow2 pointer without results. I also tried the IOmNavigator * from get_navigator on the HTMLWindow2 pointer. Any ideas?
A
This is a common cause for COM consternation. You have the window, document, or browser and you know you should be able to get the others, but everywhere you turn, QueryInterface delivers a big fat NULL. The answer lies in the mysterious IServiceProvider whose job it is, well, to provide services. IServiceProvider is a great interface: it has only one method, QueryService. If you're using ATL smart pointers, it looks like this. First, you have to get the IServiceProvider.
If all this seems confusing, there's a good reason for it. A cardinal rule of COM is that QueryInterface must always return an interface to the object queried. But the document doesn't implement IWebBrowser2; it only knows how to get the object that does. The document, browser, and window are all separate objects. In general, IServiceProvider is used whenever a bunch of separate but related COM objects together implement some kind of service. QueryInterface asks an object, do you implement this interface? QueryService tells a service provider, "get me whatever object implements this interface, please." With QueryService, the interface pointer returned may or may not be the same object as the one queried. Figure 6 illustrates the system. All the objects implement their various interfaces and store internal pointers to one another; IServiceProvider is your way to get whichever object implements a particular interface. IServiceProvider::QueryService chases the internal pointers to retrieve the object that implements the interface you want.
Figure 6 Many Objects, One IServiceProvider
IServiceProvider is essential for navigating the DHTML object hierarchy. Say you're writing an ActiveX® control and you want to navigate the object model. How do you do it? First, query your IOleClientSite for IServiceProvider like so:
CComQIPtr<IServiceProvider> isp = pSite;
then, once you have IServiceProvider, you can QueryService it for the application object.
In all these examples, SID_SWebBrowserApp identifies the service, but you'll often see code that uses IID_IWebBrowserApp as the service ID. Either will work, since <shlguid.h> #defines SID_SWebBrowserApp to IID_IWebBrowserApp, but for programming pedants SID_SWebBrowserApp is technically more correct, and more clear to anyone reading your code.
By the way, if you're brave enough to implement some far-reaching colossal object system such as the DHTML object model (Lord help you!), then you should implement IServiceProvider too.
Write a COM server (create the application with automation support, and add a property exporting the IDispatch interface of the WebBrowser control.
Another way is to register a systemwide message with RegisterWindowMessage and return the HWND of the WebBrowser control. The other application can send this message to the dialog application and use the HWND of the WebBrowser control to retrieve the IHTMLDocument interface of the HTMLDocument obejct(see MSDN
KB Q249232 HOWTO: Get IHTMLDocument2 from a HWND http://support.microsoft.com/support/kb/articles/Q249/2/32.asp).