6
votes

I am writing a WPF application that will put an icon in the system tray and, as an exercise, I want to do this without depending on System.Windows.Forms and using its NotifyIcon or NativeWindow classes.

This is fairly easy - Shell_NotifyIcon isn't hard to call from C# - and, indeed, I have succeeded in my task.

As part of this effort, I have had to create a window handle for the sole purpose of receiving messages from the system-tray. I create the native window as follows:

// Create a 'Native' window
_hwndSource = new HwndSource(0, 0, 0, 0, 0, 0, 0, null, parentHandle);
_hwndSource.AddHook(WndProc);

The message loop is hooked in AddHook() and messages are processed in a function that looks like this:

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    // Handle windows messages in this...
}

And, finally, when it comes time to destroy the thing, I close the window by posting it a WM_CLOSE message and disposing the HwndSource.

if (null != _hwndSource)
{
    UnsafeNativeMethods.PostMessage(_hwndSource.Handle, WindowMessage.WM_CLOSE, 0, 0);
    _hwndSource.Dispose();
    _hwndSource = null;
}

My question is this: the first three parameters to the constructor of HwndSource are the class style, style and extended style of the native Win32 window, respectively. For a non-visible window that will only be used as a target for window-messages, what should they be?

My defaults of zero, zero and ... er.. zero do work but I have used Spy++ to examine what Windows.Forms.NotifyIcon does and it seems that the NativeWindow it creates have the following:

Class Style:     <zero>
Styles:          WS_CAPTION, WS_CLIPSIBLINGS,
                 WS_OVERLAPPED
Extended Styles: WS_EX_LEFT, WS_EX_LTRREADING,
                 WS_EX_RIGHTSCROLLBAR, WS_EX_WINDOWEDGE

Are any of those important for a non-visible window? (I think not.)

1

1 Answers

7
votes

Windows style flags date from 1986, back when Windows v1.0 was released. There have been lots of appcompat hacks in the past 29 years and 10 major versions, Windows silently overrides style flags when the app specifies wonky ones. Nothing terribly wonky about this however, note that the value of the WS_OVERLAPPED style flag is 0. Which asks for a plain window, you automatically get the appropriate style flags for such a window.

Your HwndSource window has the exact same style flags, maybe you haven't found the correct one back in Spy++. So you don't have a problem. And no, they don't matter when the window never becomes visible.

Note a bug in your code, the WM_CLOSE message you post is never actually processed since you destroy the window right after calling PostMessage(). Just delete it, there is no point in asking the window nicely, it isn't going to object. You do however have to call Shell_NotifyIcon() with NIM_DELETE to delete the tray icon. Failure to do so leaves a "ghost" icon that only disappears when you move the mouse over it.

And do note that NotifyIcon is not as trivial as you assume it is, it has a non-obvious bug workaround that you are likely to overlook. You'll notice when the context menu refuses to close.