Blog

Moving pains

Today the site of Obs De Wilgen, the primary school where my dad works (I created the site), moved servers. We didn't want it to move servers today, but it did.

We did intend an eventual server move. We were previously located at Nedcomp, whom I would qualify as reasonable. Their support level is nothing short of great, but they're quite expensive for very little space. And since XS4ALL were offering a package which was free for schools, offered more space, more bandwidth, more features, and had the stuff we needed (ASP.NET 2.0), we were going to move to them.

But in every communication we had with them, we said: "don't move the DNS; we first want to transfer the site, only then can you change the DNS." That didn't stop them from changing it anyway, so this morning we found the site non-functional, displaying only a generic XS4ALL under construction page. What's worse, we didn't have the password.

After figuring out how to set the password, I quickly threw up a "sorry, we're down" message, frantically downloaded the whole site from the old server, and uploaded it to the new one. Then the trouble began.

XS4ALL are running the site at reduced Code Access Security permissions, which meant that a few of the assemblies I used which didn't have the AllowPartiallyTrustedCallers attribute set refused to load. Fortunately they were all my own code, so I added the attribute, recompiled and uploaded. Then it broke on some of my custom configuration sections, since apparently you need ConfigurationPermission for that which we didn't have anymore. So I had to rewrite some code to use AppSettings instead. Not nearly as nice as a custom config section, but it works.

Then I discovered none of the web services worked, which was due to this issue. Apparently we don't have write access to the temp directory. I sent them a support request on that, and in the mean time I worked around the web service stuff. All of the web services were used in the admin part of the site anyway, so it shouldn't affect regular visitors of the site.

All in all, not how I'd planned to spend my Saturday. :(

Oh, and XS4ALL's helpdesk page says they're running SQL Server 2005, while in fact it's 2000. That's not a huge issue for me, but a bit sloppy about the mistake on the helpdesk page.

Categories: General computing
Posted on: 2007-02-17 21:29 UTC. Show comments (0)

I'm famous! Again!

When I first made Find As You Type available, I was surprised by the positive feedback I got, including a mention on the IE blog.

And now, my software was included in the newsletter of a Japanese webzine, Windows Forest. The article is here (Japanese only).

This means that when I go to Tokyo in two months, I won't be able to walk down the street without getting recognized. Okay, that'll probably only happen if I wear a shirt that says "僕は「Find As You Type」の作者です" (I'm the creator of Find As You Type). And even then probably not. :P

So I'd like to thank the good people at Windows Forest for their kind reference, and to all the people visiting from Japan I'd like to say: ようこそ! Got any tips for a Dutch guy who's about to go to Japan for two years?

Categories: Personal
Posted on: 2007-02-13 17:53 UTC. Show comments (2)

Find As You Type 1.2 is out!

Well, that was a tough one! Find As You Type for Internet Explorer 1.2 is finally out. I'd started development on this as early as last November, but got distracted by a lot of other things including real work. But now, it's finally done.

Find As You Type 1.2 in Internet Explorer 7 (Click to enlarge)

It's got a whole bunch of new features, including highlight all, a configurable shortcut key, update checking, a close button, the ability to follow links you've found by pressing alt-enter, improved support for frames pages, and more.

A full list of new features, as well as download links, are available here. As before, full source is available.

I'd like to thank Eric Lawrence for his input and help.

If you have any questions or comments, please leave a comment here.

Categories: Software
Posted on: 2007-02-03 13:52 UTC. Show comments (6)

Finally, DVDs on Vista

Cyberlink released an update for PowerDVD today that fixes some of the issues it had with Windows Vista. The problems I had with blocky video are gone, and DVD-Audio works properly now.

My main complaint about SPDIF not working still isn't fixed though, but I'm not sure if it's PowerDVD or the driver writers at fault here. I logged a support incident with Cyberlink, we'll see what they have to say.

At least I won't need to boot into XP anymore for DVD-Audio and DVDs where the sound isn't that important (e.g. most older TV series). But unfortunately I'll still need XP for movies where the soundtrack is important.

At least it's a start.

Categories: General computing
Posted on: 2007-01-31 18:19 UTC. Show comments (0)

IE add-on development: globally capturing keyboard input

A long time ago, in the third article in my series on IE add-on development, I mentioned a way to globally handle keyboard input in an Internet Explorer add-in using a BHO. In this fourth installment I will talk about this.

The first thing we will need to do in order to handle keys with a BHO (Browser Helper Object) is actually write a BHO. Instead of wasting space here explaining how to do that, I will refer to this MSDN article on BHOs. That article uses ATL, while I do not, but it doesn't matter in this case.

Unfortunately the IE add-on model doesn't really offer any built-in methods for handling key input globally. What we need to do then is to use a keyboard hook, which allows us to intercept keyboard messages before they reach IE. In order to do this we must add an instance variable of type HHOOK to the class that implements our BHO (in the MSDN example this is CHelloWorldBHO, in my Find As You Type add-on it is SearchBrowserHelper; here I will call it ExampleBHO). We also need to add a static method that matches the signature of the KeyboardProc callback (if you use ATL the class definition and constructor will look different, but the rest remains the same; you must still add a HHOOK member and initialize it to NULL):

class ExampleBHO : public IObjectWithSite
{
public:
    ExampleBHO() : _hook(NULL)
    {
        /* Remaining constructor code omitted */
    }
    /* Other members omitted */
private:
    static LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);
    HHOOK _hook;
public:
    static DWORD TlsIndex; // I will explain this further down.
};

DWORD ExampleBHO::TlsIndex = 0;

We then add the following code to the implementation of IObjectWithSite::SetSite, and we implement the KeyboardProc as well:

STDMETHODIMP SearchBrowserHelper::SetSite(IUnknown *punkSite)
{
    if( _hook != NULL )
    {
        // Remove any existing hooks
        UnhookWindowHookEx(_hook);
        _hook = NULL;
    }
    
    if( punkSite != NULL )
    {
        /* Code to retrieve the IWebBrowser2 interface goes here */
        
        _hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
    }
    
    return S_OK;
}

LRESULT CALLBACK ExampleBHO::KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
    // Code < 0 should be passed on without doing anything according to the docs.
    if( code >= 0 )
    {
        MessageBox(NULL, TEXT("Keypress detected"), TEXT("ExampleBHO)", MB_OK);
    }    
    return CallNextHookEx(NULL, code, wParam, lParam);
}

Let's look at that code, shall we. A browser helper object gets instantiated for each browser window or tab. Every browser window or tab running in the same process gets its own thread, and the BHO is instantiated on that thread. So we install a keyboard hook that will get the keyboard messages for that thread by passing in the result of GetCurrentThreadId() as the last parameter.

It should be noted that if you decide to cancel key propagation (by not calling CallNextHookEx) this means the keys for which you do this will lose their original function in IE.

One thing you likely want to do in the KeyboardProc however is interact with the BHO object. But since it's a static method, that's not possible (and it has to be static since you can't create a function pointer to an instance method, as you probably know). In the earlier examples with the toolbar we solved this problem by storing a pointer in the toolbar's window data, but a BHO has no window so we can't use that approach here. And since each thread has its own BHO we can't use a global variable either. Fortunately, a solution to this problem exists with Thread Local Storage. That's what the mysterious TlsIndex member I added above was for.

To use this, we must first allocate the storage we want to use, which must be done in the DllMain function:

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch( ul_reason_for_call )
    {
    case DLL_PROCESS_ATTACH:
        ExampleBHO::TlsIndex = TlsAlloc()
        break;
    case DLL_PROCESS_DETACH:
        TlsFree(ExampleBHO::TlsIndex);
        break;
    }
    return TRUE;
}

We can now store a pointer to the BHO. Since we can guarantee our BHO will be created only once for each thread the most logical place to do this is the BHO class's constructor:

ExampleBHO() : _hook(NULL)
{
    /* Remaining constructor code omitted */
    TlsSetValue(TlsIndex, this);
}

Now we can retrieve the pointer in the KeyboardProc:

LRESULT CALLBACK ExampleBHO::KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
    // Code < 0 should be passed on without doing anything according to the docs.
    if( code >= 0 )
    {
        ExampleBHO *bho = static_cast<ExampleBHO>(TlsGetValue(TlsIndex));
        
        /* Do something with the BHO */
        
        MessageBox(NULL, TEXT("Keypress detected"), TEXT("ExampleBHO)", MB_OK);
    }    
    return CallNextHookEx(NULL, code, wParam, lParam);
}

There remains however one problem. As I said earlier, in IE7 each tab has its own thread and each BHO will set the hook on that tab's thread. Unfortunately some of the browser's chrome such as the address bar that falls outside the tabs runs on yet another thread. This thread has no browser object associated with it, so it gets no BHO, and thus no hook. Find As You Type 1.1 has this very problem, which is why the CTRL-F shortcut for Find As You Type doesn't work if the address bar has focus.

But there is a way to solve this. It involves getting the top-level window handle, retrieving its thread using GetWindowThreadProcessId, installing a hook for that thread, and using some hocus-pocus to communicate with the currently active tab and to make sure we correctly handle the existence of multiple top-level windows in the same process. That will be the topic of the next article in this series (which hopefully will not take as long as this one). And I'm pleased to say that this solution has been implemented in the soon-to-be-released Find As You Type 1.2.

Categories: Programming
Posted on: 2007-01-24 10:43 UTC. Show comments (2)

Latest posts

Categories

Archive

Syndication

RSS Subscribe

;