Easy minimization to Taskbar Notification icons!
Use TrayButton to allow your application to minimize to either a Taskbar button or to a Taskbar Notification ("SysTray") icon. TrayButton can use either a Tool window to hide the Taskbar Button or not change the window style. Usage is simple. TrayButton subclasses your window, and takes care of all the work.
Most applications should minimize to a Taskbar button, but if the purpose of the application is to change how other applications or Windows itself works, then allowing the user to minimize to a SysTray icon is reasonable. This should be the user's choice, so they aren't confused.
TrayButton was inspired by Neal Stublen's Hyperlinks.cpp on CodeGuru.com
Add TrayButton.cpp (and TrayButton.h) to your project. Call InitTrayButton() from WinMain() after creating the window and before showing it. Call SetTrayButton() when the user changes where to minimize the application. For example:
//WinMain(): //gTrayMinimize = read from registry HWND hwnd = CreateDialog( hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, Dlg_Proc ); InitTrayButton( hwnd, &iCmdShow, gTrayMinimize ); ShowWindow( hwnd, iCmdShow ); //WM_COMMAND handler: case IDC_TRAYMINIMIZE: gTrayMinimize = BST_CHECKED == IsDlgButtonChecked( hwnd, IDC_TRAYMINIMIZE ); SetTrayButton( hwnd, gTrayMinimize ); break;
There are two common ways for an application to not have a Taskbar button: use a Tool window, or have the application's main window be owned by a hidden window. TrayButton handles both of them correctly.
Tool windows work fairly well, and give an indication that something is up by their appearance. However, Tool windows can't have a minimize button, which makes minimizing them a bit obscure. Changing where to minimize to requires changing the window style, which is a bit tricky, and the window's content area will change size and needs to be adjusted. The System menu state is not maintained for Tool windows, possibly because it seemed unnecessary as they don't show an icon at the upper left corner (though a right-click on the title bar brings up the System menu).
Using a hidden owner works fine, and works with many window styles, so the visible main window can have a minimize box. However, changing where to minimize to requires either making the hidden window visible, and dealing with it having the Taskbar button and with keeping it off-screen (and with multiple monitors), or requires changing the window owner, something that works but that the Platform SDK says is impossible:
I found that Visual Basic users sometimes need to change the owner of a window, and I found some evidence that changing the owner has worked from way back. I'm not certain that it will work in the future.
Changing the owner is done in much the same way as changing the type between Popup and Tool: Hide the window, call SetWindowLong(GWL_HWNDPARENT), show the window, plus maybe one more call to show the window. That last one keeps the window from disappearing the first time it is changed from SysTray to Taskbar button (if iCmdShow had it minimized), which I think is due to documented kludges in the Platform SDK for the first call to ShowWindow().
TrayButton un-subclasses your window in WM_DESTROY. If your application crashes when closing the window, check that other subclassers are undoing their thing properly (in their WM_DESTROY handlers). Use the ASSERT in the WM_DESTROY handler to verify this.
TrayButton takes over enabling the System menu, using WM_INITMENUPOPUP, even for the cases where the system would have done it. There should be no difference between how TrayButton does is and the built-in way.
TrayButton doesn't use global variables and should be safe to use on more than one window at a time, thought probably one should not do that. It won't work if installed more than once on the same window, but really, one SysTray icon should be enough.
InitTrayButton() sets iCmdShow to SW_HIDE if the application is initially 1) minimized 2) to the SysTray.
TrayButton works with or without Unicode, however your application needs it set. I assume that you have made a global preprocessor definition if you are using Unicode.
The application small icons size used to by 16x216, 256 or 16 color, I think. In WinXP it is normally some other size, as determined by various preference settings such as the height of a window title bar. WinXP also supports "true color" (24 bit) icons, so it can do a better job of resizing. It is the caller's responsibility to set the desired size icon before calling InitTrayButton(), using either LoadIcon() or LoadImage(). I'm not sure how one should load the small size icon, but one may get better scaling if one gets the small icon size from the system:
HICON smallIcon = (HICON) LoadImage( hInstance, MAKEINTRESOUCE(IDI_APPICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON(), 0 ); SendMessage( hwnd, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon );