Posts for September 2006

IE add-on development: handling keyboard input

A little bit later than I had hoped, I now bring you the third article in my series on Internet Explorer add-on development. As promised, we're going to look at dealing with user input.

This article is really only relevant if you want to handle keyboard input, but you probably at least want to allow a user to use the arrow keys to navigate your toolbar buttons, so the odds are good that you need this if you're writing a toolbar. The Find As You Type add-on also has a textbox on the toolbar, so it needs this even more.

As it says on MSDN, if you want to handle keyboard input you must implement the IInputObject interface on your object, and when you implement IObjectWithSite::SetSite, you should QueryInterface the IUnknown object you get passed for IInputObjectSite and store this pointer somewhere.

This IInputObjectSite pointer is used to tell IE when you have the keyboard focus. It has a single method named OnFocusChangedIO, which you should call whenever your toolbar receives or loses the input focus.

This means you should handle the WM_SETFOCUS and WM_KILLFOCUS messages. Last time, we subclassed IE's rebar control, but this is not the place to handle these messages! Internet Explorer really isn't interested in you telling it when its own rebar control got focus. It wants to know when your controls got focus.

This means we must do yet more subclassing. Every control in your add-on that can get the keyboard focus must be subclassed. In the Find As You Type add-on, these are the toolbar itself and the textbox. You can use the same procedure as used previously, just substitute the window handles, window procedures and the variable used to store the old window procedure. Don't forget to put always call the original window procedure at the end of your own and to put the original back when the control is destroyed, just like last time.

In this case, you own the windows you are subclassing, so you can store the pointer to your toolbar object directly in those windows (for the toolbar, we already did this last time) and we don't need to use FindWindow in the window procedure, just call GetWindowLongPtr on the window handle that was passed to the WndProc. We can now handle the WM_SETFOCUS and WM_KILLFOCUS messages and call IInputObjectSite::OnFocusChangedIO passing true and false respectively.

In concert with this, you should implement IInputObject::HasFocusIO to return true when you have the focus and false when not. You can set a flag when handling the focus messages, or you can use GetFocus to see if one of your windows has the focus.

The more interesting part comes with TranslateAcceleratorIO. Even if you don't want to do anything special, you must call TranslateMessage and DispatchMessage here, otherwise keyboard input can break in strange ways, which tend to be inconsistent across browser version. In my case it seemed to work right in IE6, but broke in IE7. So call those two functions.

For IE7 compatibility, you should also check if the message is referring to any of the window navigation keys, such as tab, shift-tab and F6. When you get any of those keys, don't call TranslateMessage and DispatchMessage. Instead, return S_FALSE, so IE can properly respond to those keys. If you have more than one control, you can choose to handle the tab key messages to navigate between your own controls. IE6 doesn't seem to send tab key messages to toolbars, but IE7 does.

If you have any actual accelerators, here's the place to implement them. You can manually check the message, or add an accelerator table and call TranslateAccelerator. You can use this for shortcut keys and mnemonics used by your toolbar.

You might have noticed that TranslateAcceleratorIO is called by IE only when your toolbar has the input focus. It is indeed impossible to handle keyboard shortcuts if your toolbar doesn't have the focus using this method. Vista appears to have a way to solve this (although I have not tested if it actually works) with the IInputObject2 interface, which defines a TranslateAcceleratorGlobal method. This solution, if it works, would still be limited to when your toolbar is loaded (even if it doesn't need to have the focus) and would be Vista specific.

The other way to respond to keys globally that doesn't have these drawbacks is by using a Browser Helper Object and a keyboard hook. But I will leave that for a future post.

Categories: Programming
Posted on: 2006-09-28 15:12 UTC. Show comments (8)

僕は東大生です

Earlier, I wrote about how I'm trying to get the Monbukagasho (MEXT) Scholarship to go to Japan for two years.

As I briefly mentioned in that post, the next step in the process was to get admission to the University of Tokyo. After all, what's the point in getting a research scholarship if I don't have a university to go to?

Yesterday, I received an e-mail from the University of Tokyo, Graduate School of Information Science and Technology, Office of International Relations, saying that my application has been processed and I have been accepted! All that's left now, if my understanding is correct, is to get final approval for the scholarship from Monbukagakusho itself in Japan, meaning that I'm actually starting to believe I could really get it.

So, as the title of this post says (for those who can't read Japanese), "boku ha toudai-sei desu", I'm a Tokyo U student!

Categories: University, Japan
Posted on: 2006-09-21 09:10 UTC. Show comments (9)

Vista style file dialogs in .Net

Vista style file dialog

Microsoft Windows Vista has brand new common file dialogs, but unfortunately the OpenFileDialog, SaveFileDialog and FolderBrowserDialog classes from System.Windows.Forms do not use them; they still get the old-style dialogs.

Ookii.VistaDialogs is a .Net 2.0 class library that allows you to use these new-style dialogs exactly the same way as you use the normal dialogs. In addition, it has been created in such a way that you can target both Vista and older versions of Windows (which don't have this new-style dialog) without any additional effort on your part.

The curious might want to know why exactly .Net doesn't use the new style dialogs. Vista provides a new, more powerful, API for accessing the common file dialogs, and obviously .Net 2.0 doesn't use this API, but this isn't the problem. In most cases, even when you just use the old GetOpenFileName API you get the new style dialog. The thing is, the Vista style dialogs do not support hooks, so if you use a hook, you get the old style dialog. And that's where the problem lies: .Net uses a hook so it can't use the Vista style dialogs.

Unfortunately, some of the functionality provided by .Net's FileDialog classes could only be achieved using hooks with the old API, so if I wanted to use the Vista style dialogs, I'd have to use the new API, and try to replicate all the functionality that way. The benefit to this is of course that if I want to, I can easily add more advanced functionality from the new API in the future (currently, Ookii.VistaDialogs just replicates the old functionality with the new dialogs, it doesn't expose any of the new features of the new API).

Using the new API seemed simple enough; it was after all a bunch of COM objects. Unfortunately, there was no type library for these objects, and their IDL definitions were hidden in a huge (60,000 lines) IDL file. Also, one of the functions took a HWND as parameter, which would need to be a IntPtr in my library to make sure I can correctly support both x86 and x64, and I needed some methods to use the PreserveSig attribute to get the functionality I need. TlbImp isn't good at those last two.

So what I ended up doing was creating my own IDL file with just those definitions from the big file I needed, compiling that into a type library with the MIDL compiler, then importing that using TlbImp, disassembling the generated interop library with Reflector, and then manually making the changes I needed.

Then, I could finally start on the classes themself, which fortunately wasn't all that difficult (the new API really is great). These classes have the exact same public interface as the original FileDialog classes in .Net, so all you need to do is replace all instances of the original classes with my classes in your code, and you're good to go. Like I said the classes fall back if the Vista style dialog is not supported, so it supports older versions of Windows as well.

Check it out

Categories: Software
Posted on: 2006-09-19 15:25 UTC. Show comments (1)

I'm famous! Or something.

When I created Find As You Type for Internet Explorer, I didn't really expect to get any recognition for it. It is my experience that most people won't comment on software unless they have a problem with it. :)

So when several people let me know on Channel9 that they liked it, that was a welcome bonus. That some of those people worked at Microsoft or were even part of the Internet Explorer team was of course even more welcome.

But that I'd actually be mentioned on the Internet Explorer Team Blog (it's at the end of the blog post, just above the comments), that's something I never could have even imagined.

So I'm famous! Just you watch; before you know it, I'll be doing talkshows! ;)

And in case you hadn't noticed yet, version 1.1 of Find As You Type was uploaded last Wednesday.

Categories: Personal
Posted on: 2006-09-08 11:42 UTC. Show comments (3)

IE add-on development: handling toolbar messages

This is the second article in my series on the caveats of writing an IE add-on.

Last time, we looked at making your toolbar look pretty. This time, we're going to make the toolbar actually do something: we're going to see how to handle window messages from the toolbar.

If you have any experience with Win32, you know that handling window messages is fairly straight forward. You create a window procedure which you specify when you register your window class. Controls such as buttons, text boxes - and indeed toolbars - sent notification messages to their parent, usually in the form of a WM_COMMAND or WM_NOTIFY message, which the parent can handle in it's window procedure.

If we look once more at the MSDN article on IE add-ons we see that it is indeed no more difficult than that. In all the examples provided there, the add-on creates a window in the IObjectWiteSite::SetSite as a child of the window provided by IE. Toolbars are largely glossed over by that article. It is remarked that you can treat a toolbar exactly the same as a horizontal explorer bar, and that it true; all it takes is a different registry entry to turn your explorer bar into a toolbar. That's nice if you want to make your toolbar look like an explorer bar that's been shoved into a toolbar, but if you're like me, you're more interested in making a toolbar look like a toolbar.

Making a toolbar that looks like a toolbar is easy: Internet Explorer's toolbar area is a Rebar control , sometimes also called a coolbar. All you need to do to make a toolbar is to create a toolbar control as a child of the rebar (I recommend you use CreateWindowEx to do this, not the deprecated CreateToolbar approach).

The problem then is this: if you create a toolbar as a child of the rebar, the rebar gets the WM_COMMAND messages from the toolbar. And IE, not you, owns the rebar. The easy way out would seem to be to create a window in between the rebar and the toolbar. But, since that window wouldn't be transparent, you would loose the fancy gradient effects of the rebar, and that would defeat the work we did in the last post. There are some examples on the web, noticably on code project, that seem to suggest you can create an invisible child window to catch the messages, but I couldn't get those to work right. Either the toolbar remained invisible as well or IE decided to reassign the toolbar's parent to be the rebar leaving me exactly where I was. Whether this is a problem with the example or with me is something I won't go into here. ;)

So if we want the toolbar to look right, it must be a child of the rebar control. So how can we get at the messages then? The answer is subclassing. Subclassing is the practice of replacing the window procedure of a control or window with one of your own. This is done as follows:

_oldWndProc = reinterpret_cast<WNDPROC>(SetWindowLongPtr(_parentWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WndProc)));

In this line of code, we are assigning our own window procedure, named WndProc (which is a static member of our class) to be the window procedure for the parent window that we got from IE (using IOleWindow::GetWindow), which for a toolbar is the rebar control. SetWindowLongPtr returns the old window procedure, which we store in a class member variable named _oldWndProc.

When implementing the WndProc function, we will need a way to get at the data from our toolbar; you'll likely want to interact with it, and at the least you'll need it to get the old window procedure, because it's very important to call that. Since the window procedure must be a global function or a static member, it can't do that without some additional effort. The MSDN article on toolbars uses a simple trick for this: use SetWindowLongPtr to store a pointer to the class instance for the toolbar in the window. We can still use this approach, but it is very imporant that you do not store this pointer in the rebar. There is only one user data slot for a window, so imagine if every toolbar tried to store their pointer in the rebar.

Instead, we store this pointer in the toolbar window. This means that in the window procedure, we must be able to find our toolbar window. Since the toolbar window uses the standard toolbar class name, that isn't enough. So to be able to identify it, we also give it a caption (which won't be visible so it can be anything, just make sure it's reasonably sure to be unique). Passing this text to CreateWindowEx when creating the toolbar didn't seem to work for some reason, so I used SetWindowText instead.

Next we get to implementing the window procedure. What we need to do is this: find the toolbar window, extract the pointer to the toolbar class, handle any messages from the toolbar, and call the old window procedure to make sure we don't break IE.

LRESULT CALLBACK SearchBar::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
    // Find the toolbar window
    HWND barWindow = FindWindowEx(hWnd, NULL, TOOLBARCLASSNAME, L"Example toolbar window text");
    ToolBar *bar = reinterpret_cast<SearchBar*>(GetWindowLongPtr(barWindow, GWLP_USERDATA));

    switch( uMessage )
    {
    case WM_COMMAND:
        if( reinterpret_cast<HWND>(lParam) == barWindow )
        {
            // Handle the message, and don't call the rebar's original window procedure
            return 0;
        }
        break;
    }
    return CallWindowProc(bar->_oldWndProc, hWnd, uMessage, wParam, lParam);
}

Make sure you only handle messages that actually come from your toolbar. Call the original window procedure for all other messages.

The last thing to do is make sure that before the toolbar is destroyed, we put the original window procedure back. If you don't do this, IE will end up calling your window procedure, which then tries to use a pointer to the ToolBar class that is no longer valid, crashing IE. The best place to do this is the destructor.

ToolBar::~ToolBar()
{
    if( _oldWndProc != NULL )
        SetWindowLongPtr(_parentWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(_oldWndProc));
}

That's it. We've now got a toolbar that looks like a toolbar should in IE, and we can handle messages from it so we can respond if somebody clicks a button. I want to stress that you should be very careful in doing this. You're messing with IE's inner workings here; if you get it wrong, IE will likely crash or misbehave some other way. And our goal was to extend IE, not to break it (unless you're writing a spyware toolbar, in which case I'd kindly ask you to get off my site :P ). If you want more details on how I implemented it, you can also check out the source code of the Find As You Type for Internet Explorer add-on.

That was a long one! Next time, we'll look at keyboard input. We'll do some more subclassing, and figure out how to implement IInputObject::TranslateAcceleratorIO.

Categories: Programming
Posted on: 2006-09-01 18:55 UTC. Show comments (4)

Latest posts

Categories

Archive

Syndication

RSS Subscribe

;