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.
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.
As promised, this is the first in what will be a few articles about some of the pitfalls involved in writing IE add-ons, based on my own experience in writing Find As You Type for Internet Explorer. This isn't meant to be a tutorial or a definitive guide to writing add-ons; it's assumed you have at least some knowledge on how to do this. For a review of the basics, you can check this article on MSDN.
A list of the articles in this series is available here.
The first item I will cover is visual styles. Suppose you were to write you own add-on based on the guide at the link above, or even if you just compile the samples from MSDN or CodeProject or similar, and then run them. You will soon notice that your toolbar or explorer bar doesn't use the Windows XP theme (or the Vista theme on Vista), or visual style. Instead it will use the Windows Classic look. Here's what the Find As You Type toolbar looks like without visual styles:
As you can see, this looks quite out of place, because the rest of Internet Explorer is using the proper theme.
So how to go about fixing this. Unfortunately, the answer to that is quite hard to find. Some casual googling will not reveal it (although it can be found that way, but it's quite buried). Of course it's my hope that if you googled this problem, you ended up on this page. :)
Fortunately, once you know the answer, it's quite simple to actually do it. The answer is described in Microsoft Knowledge Base article 830033, which, although it says it's about Office add-ons, it also applies to Internet Explorer add-ons.
As it turns out, you must add a special manifest resource to your application. This is quite similar to how you'd make any application support visual styles. The difference is that, instead of the normal CREATEPROCESS_MANIFEST_RESOURCE_ID, you must use the ISOLATIONAWARE_MANIFEST_RESOURCE_ID.
Note that if you are using Visual Studio 2005, you don't actually need to create (or modify) a resource file like it specifies in the article. You can simply add the manifest file to your Visual C++ Win32 DLL project, and it will be embedded automatically. For DLL projects, Visual Studio 2005 automatically uses the ISOLATIONAWARE_MANIFEST_RESOURCE_ID to do this. After this is done, Find As You Type looks like this.
And that, of course, looks much nicer. There remains one problem that I do not yet know how to solve. If you open any windows from your add-on, for instance using the Win32 MessageBox or DialogBox function, they will still not have visual styles. Considering that even Microsoft's own Internet Explorer Developer Toolbar suffers from this problem, I don't know if there even is a solution for that. But if anyone knows how, I'd appreciate it if they'd let me know.
That concludes my first IE add-on article. Next time, we'll look at how to handle messages if you're writing a toolbar.
I'm proud to announce a brand new download on ookii.org: Find As You Type for Internet Explorer.
Find As You Type provides "interactive search" capabilities similar to Firefox and Emacs, but for Internet Explorer. It works on Internet Explorer 5 and higher (including IE7, of course). It'll work on x86 and x64 versions of Windows.
This has been my first venture into creating an IE add-on, and also the first big unmanaged C++ Win32 project I've done in a long while. Although MS is keen to emphasise that IE, like Firefox, can indeed be extended (and not just for spyware either!), but it's difficult, very difficult.
Over the next few days I intend to make a few blog posts documenting some of the pitfalls I've encountered. In particular I'll cover those issues that are not only under-documented on MSDN, but whose answer can't be easily found using a Google search.
The following is a list of these articles. I will add links as more articles become available.
Last Thursday I had to go to the Japanese Embassy. Why would I go there, you ask? Because I'm trying to get a scholarship. The Japanese Government (Monbukagakusho) Scholarship for research students to be exact. Monbukagakusho is the Ministry of Education, Culture, Sports, Science, and Technology, commonly abbreviated as MEXT. This scholarship is for going to Japan for two years as a research student, and is for pretty much any field, not just computer science. At most three scholarships are awarded each year in the Netherlands.
So last Thursday I had to go to the Japanese Embassy for the preliminary selection process. I had to be there at 9:00, so I had to get up at 6:30 - which is really early, for me anyway. I got up, had a quick breakfast, put on my best (and only) suit, and set off for The Hague using public transportation, furiously hoping there wouldn't be any delays, and that it wouldn't rain (since that'd be hell on my suit).
Fortunately, all went exactly as planned, and at 8:45 I arrived at the Embassy, where they told me to wait outside because they weren't open yet. Too bad arriving exactly on time is nearly impossible when using public transportation. I was also first, but soon a few others showed up. In total, there were six applicants, which since there are at most three scholarships meant I had to killoutperform three of them.
At 9:15 sharp we started. First up were exams. Our knowledge of both English and Japanese would be tested in a total of four exams: one English, three Japanese with increasing difficulty. The English exam was easy. The only difficult thing about it was that some of it was really weird English (the most difficult questions were of the type "which of the following four sentences is wrong?" They were all wrong!) but it was still not much of a challenge.
Then came Japanese. Note that it's not actually required to speak Japanese to get this scholarship, so the exams were just to check my current proficiency. The three exams were labelled simply Japanese A, B and C but as far as I gathered they were equivalent of sankyuu, nikyuu and ikkyuu (third, second and first level Japanese, with first being the highest), which are standard Japanese Language Proficiency Tests. The first one went pretty well. The first part was downright easy, although the last text was slightly more difficult. But, I could understand nearly all of it, and didn't have to guess any of the questions.
The second one was a lot more difficult. The number of words and kanji that I didn't know increased drastically, which made answering some of the questions really hard. I think the ratio of knowing to guessing was about 40 to 60. Which may seem bad, but it was along my expectations. After a brief lunch came the third exam, which was extremely difficult. If it really was ikkyuu that's not surprising though. Overall I feel I could have answered this one just as good if they hadn't given me the questions, just the answer sheet. So I guessed pretty much all of it. Again, bad, but as expected. Being thoroughly exhausted by now didn't help either.
Then, the six of us were brought down to the Embassy's library and told to wait until 15:00, and then we'd be called up one by one for an interview which would last 15-20 minutes.
Exactly how they determined the order of interviewing us I didn't know. It wasn't by first name, by last name, by birthdate or any other discernable pattern, so I guess it was random. In any case, I was third to go. I can't recall ever being more nervous in my entire life. The nerves combined with the mental exhaustion from four hours of exams meant that I suddely felt I would have less difficulty competing in the winter olympics than doing this interview. Unfortunately, there weren't any skating rings available, but there was a really big table with a lot of chairs around it, and four people on one side - two from the Embassy, one professor from Leiden (not from the CompSci department of course) who was on the committee, and one from Nuffic (the foundation that coordinates these scholarships in the Netherlands), and me on the other side.
After brief introductions, they started asking questions. You could say the questions fell into two categories: the first bit was about my research proposal, the second about my motivation and my other plans (in Japan and for the future in general). The part about my proposal went really well. They seemed to think my proposal was pretty good (the professor from Leiden called it "enthousiastic") and I could answer their questions without much problems. I do, after all, know what I'm talking about (although the American style of job interviews where you have to have an air of "I'm the best person for this job that exists" doesn't work in Japan, a degree of modesty is better, or so I'm told). The other part went, well, less good. I had anticipated the sort of questions they would ask, and had prepared answers. Unfortunately, I couldn't remember any of them. So I winged it, best as I could. I can't remember half of what I said, it must have made some sense though (see below).
After what felt like 30 seconds but what was more like 20 minutes I went back to the library. I could leave at this point if I wanted, but we had decided we would wait for each other. Finally, around 17:00, all of us had been interviewed, after which we left.
All in all, I was reasonably satisfied. Sure, there's a ton of things I could've said but didn't, but no point in worrying about that now. Whether it would be good enough, I had no idea whatsoever. I didn't know how the rest had done on the interviews, and I didn't know what their criteria would be. In any case, I was exhausted, and spent the evening doing exactly nothing and slept better than I have in a long time.
Although final approval for the scholarship depends on a number of things (such as admission to the University in Japan, in my case Tokyo University) and wouldn't be known until around January, we would be told if we had been tentatively selected the next week. Tentative selection means we passed the interview and exam portion, and would be able to apply for admission and the rest.
Naturally, next week last Thursday is this week, and I just received a call. I made it through! So now comes the real work, getting admission etc. (not to mention, graduating :P ). It's not final (I'm not even sure if we're already narrowed down to three candidates or if that comes later, since we said we'd keep in touch I suppose I'll find out), but it's a big step in the right direction.
So yeah, I'm pretty pleased right now. :)