Thursday, December 20, 2007

Repairing Internet Explorer

Creating Internet Explorer extensions is not always an easy task. Erroneous extensions can make your IE to crash, become unstable or to behave unpredictably.

Even worse, uninstalling extensions can leave IE in a broken state, some things stop working properly. This usually happens when IE components are unregistered and IE registry keys are altered and not properly restored on uninstall. Here's a short list of damages I personally encountered:

1). 'Open in New Window' Command Does Not Work in Internet Explorer
I'll come later with examples of how an extension can break IE and how to avoid it. For now let's concentrate on how we can fix it. Download RepairIE.zip file, extract files inside and run "fixie.cmd"

Points of interest:
  • Internet Explorer components are registered using regsvr32.exe tool.
  • For IE7 mshtml.dll can not be registered as explained above; instead mshtml.tlb is registered using regtlib.exe tool.
  • Registering shdocvw.dll using regsvr32.exe is not a good idea on IE7 because it damages [HKEY_CLASSES_ROOT\Typelib\{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}\1.1\0\win32 registry key.
  • reg_ieframe.reg file fixes the broken key above.

Sunday, December 09, 2007

Allow local scripted HTML files to run in IE7

During automatically testing Twebst library I was very annoyed about IE7 refusing to properly open local HTML files that contains scripts. The following message is displayed:

"To help protect your security, Internet Explorer has restricted this web page from running scripts or ActiveX controls that could access your computer. Click here for options..."

It took me several minutes to find the hidden option that turns off this warning. First I looked for it in Security tab options but it was actually in Advanced tab. Here's how you find it:

1). Go to:
Tools > Internet Options > Advanced > Security
2). Check:
Allow active content to run in files on My computer
3). Restart IE





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.

Sunday, October 28, 2007

Open TextRange selection as URL

On many forums, people post URLs as plain text and this forces me to:
  • select the text URL
  • ctrl+C to copy it in the clipboard
  • ctrl+T to open a new tab
  • paste the URL in the address bar of the newly created tab
  • press "Enter" key to start navigation

I found this whole process very annoying and it seems I have to go through this many times a day. Here's my attempt to automate it by creating an extension for "Internet Explorer" similar to Linkification extension (for "Fire Fox" browser). I called my extension: "Make Link".

How it works:

  • right click on text URL
  • choose "MakeLink: use clicked text as link" menu item (the URL is open in a new tab automatically)

or you can

  • fully select the URL text or only a part of it
  • right click on selected text
  • choose "MakeLink: use clicked text as link" menu item (text link is open in a new tab automatically)

DOWNLOADS:

Points of interests:

  • HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt registry key to add a item in the IE context menu
  • gain access to window object passed in external.menuArguments
  • document.selection property to get the selected TextRange
  • TextRange methods: moveStart, moveEnd, moveToPoint, select
  • The extension is entirely written in JScript (no BHO, no COM).

Known issues:

  • on some web pages with horizontal scroll simply right click a text URL won't work. You need to select a part of the URL and then right click it.



Wednesday, October 17, 2007

How to properly catch RBN_CHEVRONPUSHED notification?

This is actually a thread I started on MSDN forum but unfortunately it remained unanswered:

Virtually any IE toolbar needs a chevron to happily live along with other toolbars in the same re-bar. So does my toolbar. To implement chevron functionality in IE toolbars I need to handle RBN_CHEVRONPUSHED. According to MSDN, when the chevron button is pushed, the notification is sent by the rebar in the form of WM_NOTIFY message to its parent. Here is the windows hierarchy in IE7:

WorkerW <- ReBarWindow32 <- ToolbarWindow32

where the last toolbar window is my toolbar. So I need to catch notifications from ReBarWindow32 that are sent to WorkerW window. To do that the first idea that came to my mind was to subclass the WorkerW window. I don’t like this idea because:
  • I subclass a window that does not belong to me, it was created by IE.
  • I don’t know what is the best time to subclass it: on IObjectWithSite.SetSite or on IDockingWindow.ShowDW ? (Those functions are implemented by my toolbar component)
  • I don’t know what is the best time to un-subclass it.
  • I don’t know when other toolbar might subclass/un-subclass the same window (I actually got a conflict with other toolbar resulting in IE stack overflow crash because of the order of subclassing/unsubclassing).
My second approach uses RB_SETPARENT to modify the parent of ReBarWindow32 window to be one of my windows. I process the RBN_CHEVRONPUSHED notification for my chevron button and send the other notifications to the original parent window (that is WorkerW). I change the parent on toolbar initialization/un-init (IObjectWithSite.SetSite). It seems a safer approach but I’m still worried about other toolbars using the same technique and the possibility of conflicts.

Take care of standard IE "Links" toolbar that also sends WM_COMMAND, WM_DRAWITEM and WM_MEASUREITEM messages to WorkerW window (and now you'll get those messages too). On IE6, "Go" button also do the same.

So the question remains: what is the best way to catch RBN_CHEVRONPUSHED notification when creating an IE toolbar extension?

Wednesday, October 10, 2007

When IHTMLWindow2::get_document returns E_ACCESSDENIED

Internet Explorer extensions usually needs to access HTML elements. When extensions are initialized they get a IWebBrowser2 pointer representing the browser. Starting with this pointer one can get any HTML element in the web page but to do that we need to browse a hierarchy of frames first. The simplest web pages only have one frame and one document. Web pages containing <frame> or <iframe> have a hierarchy of frames, each frame having its own document.

Here are the objects involved and the corresponding interfaces:
browser      - IWebBrowser2
frame/iframe - IHTMLWindow2
document - IHTMLDocument2
element - IHTMLElement


The list bellow shows what method to call to get one object from another:
browser      -> document        IWebBrowser2::get_Document
document -> frame IHTMLDocument2::get_parentWindow
frame -> document IHTMLWindow2::get_document
frame -> parent frame IHTMLWindow2::get_parent
frame -> children frames IHTMLWindow2::get_frames


A normal call chain to get a HTML element is:
browser -> document -> frame -> child frame -> ... -> child frame -> document -> element

This will work almost all the time. The problems arise when different frames contain documents loaded from different internet domains. In this case IHTMLWindow2::get_document returns E_ACCESSDENIED when trying to get the document from the frame object. I think this happens to prevent cross frame scripting atacks.

Here is HtmlWindowToHtmlDocument function I wrote to be used instead IHTMLWindow2::get_document to bypass the restriction:



// Converts a IHTMLWindow2 object to a IHTMLDocument2. Returns NULL in case of failure.
// It takes into account accessing the DOM across frames loaded from different domains.

CComQIPtr<IHTMLDocument2> HtmlWindowToHtmlDocument(CComQIPtr<IHTMLWindow2> spWindow)
{
ATLASSERT(spWindow != NULL);

CComQIPtr<IHTMLDocument2> spDocument;
HRESULT hRes = spWindow->get_document(&spDocument);

if ((S_OK == hRes) && (spDocument != NULL))
{
// The html document was properly retrieved.
return spDocument;
}

// hRes could be E_ACCESSDENIED that means a security restriction that
// prevents scripting across frames that loads documents from different internet domains.

CComQIPtr<IWebBrowser2> spBrws = HtmlWindowToHtmlWebBrowser(spWindow);
if (spBrws == NULL)
{
return CComQIPtr<IHTMLDocument2>();
}

// Get the document object from the IWebBrowser2 object.
CComQIPtr<IDispatch> spDisp;
hRes = spBrws->get_Document(&spDisp);
spDocument = spDisp;

return spDocument;
}


// Converts a IHTMLWindow2 object to a IWebBrowser2. Returns NULL in case of failure.
CComQIPtr<IWebBrowser2> HtmlWindowToHtmlWebBrowser(CComQIPtr<IHTMLWindow2> spWindow)
{
ATLASSERT(spWindow != NULL);

CComQIPtr<IServiceProvider> spServiceProvider = spWindow;
if (spServiceProvider == NULL)
{
return CComQIPtr<IWebBrowser2>();
}

CComQIPtr<IWebBrowser2> spWebBrws;
HRESULT hRes = spServiceProvider->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&spWebBrws);
if (hRes != S_OK)
{
return CComQIPtr<IWebBrowser2>();
}

return spWebBrws;
}
Here is the C# version of the code: "When IHTMLWindow2.document throws UnauthorizedAccessException".

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

Friday, October 05, 2007

Quick and dirty

One of the most interesting comments I ever found in source code is
//QD:

I am familiar with the well known //TODO: style of comment used by Visual Studio, but QD was new to me. As I found later, QD stands for "quick and dirty" and it is basically a low quality piece of code that will cause you a lot of trouble in the future. A quick search throughout the sources revealed plenty of QD comments. It seemed someone made a way of living out of this.
But sometimes programmers gotta do what programmers gotta do and even if I don't like it, I have to be quick and dirty.

Few days ago I was working on Windows mobile and I needed to decode a string that was HTTP posted to a web server. This is best accomplished by HttpUtility.UrlDecode method. Out of luck! HTTPUtility class is not available on .Net Compact Framework.

Here is where .Net Reflector came to help. I simply decompiled UrlDecode method in .Net Framework and copy-pasted the code to mobile project. Luckily, the generated code was short and it doesn't reference a lot of other classes. I was amazed by the quality of the decompiled code. Reflector did a great job!

Yes, it was quick, it was dirty but also it was very effective. As a bonus, the code is better than any code I can possibly produce in this very short time. After all, it was tested by Microsoft QA team!

Wednesday, October 03, 2007

From IAccessible to IHTMLElement and back

When developing Internet Explorer plug-ins you might want to take advantage of the dual nature of the browser objects. Internet Explorer exposes the DOM (document object model) using IHTMLElement interface as the building block of the hierarchy. It also offers accessible objects through Active Accessibility IAccessible interface.


// From IAccessible to IHTMLElement.
CComQIPtr<IHTMLElement> AccessibleToHTMLElement(IAccessible* pAccessible)
{
ATLASSERT(pAccessible != NULL);

// Query for IServiceProvider interface.
CComQIPtr<IServiceProvider> spServProvider = pAccessible;
if (spServProvider != NULL)
{
// Ask the service for a IHTMLElement object.
CComQIPtr<IHTMLElement> spHtmlElement;
HRESULT hRes = spServProvider->QueryService(IID_IHTMLElement, IID_IHTMLElement,
(void**)&spHtmlElement);

return spHtmlElement;
}

return CComQIPtr<IHTMLElement>();
}

// From IHTMLElement to IAccessible.
CComQIPtr<IAccessible> HTMLElementToAccessible(IHTMLElement* pHtmlElement)
{
ATLASSERT(pHtmlElement != NULL);

// Query for IServiceProvider interface.
CComQIPtr<IServiceProvider> spServProvider = pHtmlElement;
if (spServProvider != NULL)
{
// Ask the service for a IAccessible object.
CComQIPtr<IAccessible> spAccessible;
HRESULT hRes = spServProvider->QueryService(IID_IAccessible, IID_IAccessible,
(void**)&spAccessible);

return spAccessible;
}

return CComQIPtr<IAccessible>();
}


Not all IHTMLElement objects support Active Accessibility. Here is the list of the HTML elements that are also accessible elements:
A, AREA, BUTTON, INPUT type=BUTTON, INPUT type=RESET, INPUT type=SUBMIT, FRAME, IMG, INPUT type=checkbox, INPUT type=image, INPUT type=password, INPUT type=radio, MARQUEE, OBJECT, APPLET, EMBED, SELECT, TABLE, TD, TH, TEXTAREA, INPUT type=TEXT.

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

Tuesday, July 17, 2007

COM number speller

Some time ago, I was asked to provide a way to print numbers as text in Romanian language. Interestingly, I couldn't find an easy way to do this in Excel (Office 2003 as far as I remember). Searching the internet, I found other people having the same problem. Microsoft provides a solution for English language, but not for Romanian.

I decided to create a reusable piece of code that will be easily used in as many environments as possible. I chose COM and ATL to be the solution to my problem. This is also a good programming exercise for my rusty COM / C++ skills and I plan to use it as a template for all my future COM objects.

Points of interest:
  • error info support by implementing IErrorInfo interface.
  • Help in CHM format and context identifiers specified in MIDL source file.
  • BSTR manipulation using CComBSTR class provided by ATL.
  • IDispatch support, so the component can be used from scripting environments.
  • Spelling implementation itself and support for multiple languages (only Romanian and English for now).

To install the COM object just simply run Install.bat
Here is the Excel macro that makes use of NumberSpeller COM object:

Function Spell(n As Currency) As String
'Create the speller object.
Dim s As NumberSpellerLib.speller
Set s = New NumberSpellerLib.speller

Dim o As Object
Set o = s
o.Language = "ro"
Spell = o.Translate(n)
End Function


Downloads:

Sunday, May 27, 2007

Blogs and C++ syntax highlighting

This wasn't supposed to be my first post. I wanted to start my blogging existence writing about something else. It proved that being a blogger is not as easy as it should be, especially when trying to post some C++ code. The online editor allows to directly modify the HTML code but this is not the simplest or most enjoyable thing to do. Because I wanted C++ syntax highlighting so hard I decided to create a little tool to automate this task as much as possible and to allow me to focus on the real subject.

The result is cpptohtml.exe that takes a
.cpp file as a command line argument and generates a .html file. If no file name is provided as argument, a standard "file open" dialog allows the user to specify the source .cpp file. The tool also accepts /C as command line arguments. When used the source code is taken from clipboard and the HTML result is put also in clipboard.

I found creating a windows shortcut to cpptohtml.exe very useful. This way I can launch the tool by pressing a shortcut key combination (CTR+ALT+T).
I'm gonna use this tool like this:
  • open preferred IDE and copy the C++ source code into the clipboard.
  • launch the tool using the shortcut key combination. The HTML result is in the clipboard.
  • paste the formated HTML in the online blog editor.
Download:
You can find here the full source code and the executable. I used Visual C++ and LEX. The archive also contains a .js script to automate the shortcut creation.
I am pretty happy with the final result and how C++, LEX and JScript combines to automate a boring repetitive task.

Beautiful the technology is!