Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RegisterHotKey not working with invisible forms (c#)

I'm trying to put an icon in the system tray and then give it a global keyboard shortcut to carry out a function.

I'm using RegisterHotKey to set the global keyboard shortcut, and it works if the main form associated with the icon is visible. But if the form is invisible then the WndProc method is never invoked.

Any ideas?

Edit: What I mean by "hidden" is that the following is added to the main form:

protected override void OnLoad(EventArgs e)
{
    hotKey = new GlobalHotkey(GlobalHotkey.WIN, Keys.T, this);
    bool registered = hotKey.Register();
    Visible = false;
    ShowInTaskbar = false;
    base.OnLoad(e);
}

"registered" is showing as "true", and the shortcut key works fine if I leave out the "Visible = false;" and the "ShowInTaskbar = false;".

like image 545
Andrew Ducker Avatar asked May 11 '12 14:05

Andrew Ducker


2 Answers

The problem is that setting ShowInTaskbar to false changes the window handle, which means that the hwnd passed to RegisterHotkey is no longer valid.

Registering the hotkey after setting ShowInTaskBar works fine.

like image 137
Andrew Ducker Avatar answered Sep 17 '22 14:09

Andrew Ducker


Winforms works around a pretty draconian restriction in the winapi. Some properties of a window can only be specified when a window is created and can't be changed later. Or in other words, they are specified in the native CreateWindowEx() call.

It works around it by calling CreateWindowEx() again. Or in other words, destroy the existing window and create it again. That's a nifty trick but it does have some side effects. You can see a wee bit of flicker for example when the new window paints itself. Some bigger side effects are visible on for example a TreeView. All the nodes collapse when it gets recreated. Hard to avoid, there is just too much state associated with the original window. For a Form, the ShowInTaskbar property is one such property. But also RightToLeft, FormBorderStyle, ControlBox, etcetera.

The most relevant side-effect is the one you are running into. Recreating the window always changes the Handle property, inevitably. And that goes wrong when you use RegisterHotKey(), or a library that uses it, that winapi call uses the window handle. So when Winforms destroys that window there will never again be a callback.

It is easy to fix, you are just using the wrong event handler. Make the call in an override for the OnHandleCreated method instead. It re-runs when the window gets re-created. Yet another easy fix, but not nearly as reliable, is to only set properties like ShowInTaskbar in the constructor.

like image 40
Hans Passant Avatar answered Sep 17 '22 14:09

Hans Passant