Wednesday, November 14, 2007

How to get a handle to current TabWindowClass tab in IE7

IWebBrowser2::get_HWND method gets the handle of the Internet Explorer 7 main window. Sometimes the tab window handle is needed. Here's a sample code that I recently found in MSDN. It shows how to get the handle of the tab window starting from a IWebBrowser2 object.
#include <shlguid.h>

HWND GetTabWnd(CComQIPtr<IWebBrowser2> spBrowser)
{
HWND hwndTab = NULL;
CComQIPtr<IServiceProvider> spServiceProvider = spBrowser;

if (spServiceProvider != NULL)
{
CComQIPtr<IOleWindow> spWindow;
if (SUCCEEDED(spServiceProvider->QueryService(
SID_SShellBrowser,
IID_IOleWindow,
(void**)&spWindow)))
{

spWindow->GetWindow(&hwndTab));
}
}

return hwndTab;
}
I think the code is supposed to work on top level IWebBrowser2 objects. You can read more about top browser objects in my previous article.

This technique was successfully implemented and tested in Twebst web automation library.

Monday, November 12, 2007

When IWebBrowser2::get_HWND returns E_FAIL

IWebBrowser2::get_HWND "gets the handle of the Microsoft Internet Explorer main window". As any COM method get_HWND returns a HRESULT value. According to MSDN, the method "returns S_OK if successful, or an error value otherwise".

It was hard for me to imagine how this method could fail but I still got an E_FAIL return value. This happened because the IWebBrowser2 object was not the top level browser. A web page containing frames/iframes is represented by a hierarchy of IHTMLWindow objects. Each window has an associated IHTMLDocument2 object exposed by IHTMLWindow2::get_document. An IHTMLWindow can be also converted to a IWebBrowser2 object. Here's my solution to get the main window handle starting from a non-top level browser object (this is a common scenario when adding your custom menu item in the IE context menu).

// IHTMLWindow2 to IWebBrowser2
CComQIPtr<IWebBrowser2> IHTMLWindow2ToIWebBrowser2(CComQIPtr<IHTMLWindow2> spHTMLWindow)
{
ATLASSERT(spHTMLWindow != NULL);

// Query for a service provider.
CComQIPtr<IWebBrowser2> spBrowser;
CComQIPtr<IServiceProvider> spServiceProvider = spHTMLWindow;

if (spServiceProvider != NULL)
{
// Ask the service provider for a IWebBrowser2 object.
spServiceProvider->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&spBrowser);
}

return spBrowser;
}

// IWebBrowser2 to IHTMLWindow2
CComQIPtr<IHTMLWindow2> IWebBrowserToIHTMLWindow(CComQIPtr<IWebBrowser2> spBrowser)
{
ATLASSERT(spBrowser != NULL);
CComQIPtr<IHTMLWindow2> spWindow;

// Get the document of the browser.
CComQIPtr<IDispatch> spDisp;
spBrowser->get_Document(&spDisp);

// Get the window of the document.
CComQIPtr<IHTMLDocument2> spDoc = spDisp;
if (spDoc != NULL)
{
spDoc->get_parentWindow(&spWindow);
}

return spWindow;
}


CComQIPtr<IWebBrowser2> TopBrowser(CComQIPtr<IWebBrowser2> spBrowser)
{
ATLASSERT(spBrowser != NULL);

// Retrieve IHTMLWindow2 from browser.
CComQIPtr<IHTMLWindow2> spHTMLWnd = IWebBrowserToIHTMLWindow(spBrowser);
if (spHTMLWnd != NULL)
{
// Find top window.
CComQIPtr<IHTMLWindow2> spTopWindow;
HRESULT hResult = spHTMLWnd->get_top(&spTopWindow);

if (SUCCEEDED(hResult) && (spTopWindow != NULL))
{
// Convert the browser object to window.

return IHTMLWindow2ToIWebBrowser2(spTopWindow);
}
}

return CComQIPtr<IWebBrowser2>();
}

This technique was successfully implemented and tested in My web automation library.

Monday, November 05, 2007

How to programmatically find if XP theme is active?

Registry key:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\ThemeManager

Value:
ThemeActive is "1" if Windows XP, "0" if Windows Classic.