! OK. Finally
Download the esnips code package to get green_man.ico!
///////////////////////////////////////////// // // // Minimizing C++ Win32 App To System Tray // // // // You found this at bobobobo's weblog, // // https://bobobobo.wordpress.com // // // // Creation date: Mar 30/09 // // Last modified: Mar 30/09 // // // ///////////////////////////////////////////// // GIVING CREDIT WHERE CREDIT IS DUE!! // Thanks ubergeek! http://www.gidforums.com/t-5815.html #pragma region include and define #include <windows.h> #include <shellapi.h> #include <stdio.h> #ifdef UNICODE #define stringcopy wcscpy #else #define stringcopy strcpy #endif #define ID_TRAY_APP_ICON 5000 #define ID_TRAY_EXIT_CONTEXT_MENU_ITEM 3000 #define WM_TRAYICON ( WM_USER + 1 ) #pragma endregion #pragma region constants and globals UINT WM_TASKBARCREATED = 0 ; HWND g_hwnd ; HMENU g_menu ; NOTIFYICONDATA g_notifyIconData ; #pragma endregion LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); #pragma region helper funcs // These next 2 functions are the STARS of this example. // They perform the "minimization" function and "restore" // functions for our window. Notice how when you "minimize" // the app, it doesn't really "minimize" at all. Instead, // you simply HIDE the window, so it doesn't display, and // at the same time, stick in a little icon in the system tray, // so the user can still access the application. void Minimize() { // add the icon to the system tray Shell_NotifyIcon(NIM_ADD, &g_notifyIconData); // ..and hide the main window ShowWindow(g_hwnd, SW_HIDE); } // Basically bring back the window (SHOW IT again) // and remove the little icon in the system tray. void Restore() { // Remove the icon from the system tray Shell_NotifyIcon(NIM_DELETE, &g_notifyIconData); // ..and show the window ShowWindow(g_hwnd, SW_SHOW); } // Initialize the NOTIFYICONDATA structure. // See MSDN docs http://msdn.microsoft.com/en-us/library/bb773352(VS.85).aspx // for details on the NOTIFYICONDATA structure. void InitNotifyIconData() { memset( &g_notifyIconData, 0, sizeof( NOTIFYICONDATA ) ) ; g_notifyIconData.cbSize = sizeof(NOTIFYICONDATA); ///// // Tie the NOTIFYICONDATA struct to our // global HWND (that will have been initialized // before calling this function) g_notifyIconData.hWnd = g_hwnd; // Now GIVE the NOTIFYICON.. the thing that // will sit in the system tray, an ID. g_notifyIconData.uID = ID_TRAY_APP_ICON; // The COMBINATION of HWND and uID form // a UNIQUE identifier for EACH ITEM in the // system tray. Windows knows which application // each icon in the system tray belongs to // by the HWND parameter. ///// ///// // Set up flags. g_notifyIconData.uFlags = NIF_ICON | // promise that the hIcon member WILL BE A VALID ICON!! NIF_MESSAGE | // when someone clicks on the system tray icon, // we want a WM_ type message to be sent to our WNDPROC NIF_TIP; // we're gonna provide a tooltip as well, son. g_notifyIconData.uCallbackMessage = WM_TRAYICON; //this message must be handled in hwnd's window procedure. more info below. // Load da icon. Be sure to include an icon "green_man.ico" .. get one // from the internet if you don't have an icon g_notifyIconData.hIcon = (HICON)LoadImage( NULL, TEXT("green_man.ico"), IMAGE_ICON, 0, 0, LR_LOADFROMFILE ) ; // set the tooltip text. must be LESS THAN 64 chars stringcopy(g_notifyIconData.szTip, TEXT("Green man.. here's looking at ya!")); } #pragma endregion int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR args, int iCmdShow ) { TCHAR className[] = TEXT( "tray icon class" ); // I want to be notified when windows explorer // crashes and re-launches the taskbar. the WM_TASKBARCREATED // event will be sent to my WndProc() AUTOMATICALLY whenever // explorer.exe starts up and fires up the taskbar again. // So its great, because now, even if explorer crashes, // I have a way to re-add my system tray icon in case // the app is already in the "minimized" (hidden) state. // if we did not do this an explorer crashed, the application // would remain inaccessible!! WM_TASKBARCREATED = RegisterWindowMessageA("TaskbarCreated") ; #pragma region add a console // add a console, because I love consoles. // To disconnect the console, just comment out // the next 3 lines of code. //// AllocConsole(); //// AttachConsole( GetCurrentProcessId() ) ; //// freopen( "CON", "w", stdout ) ; #pragma endregion #pragma region get window up WNDCLASSEX wnd = { 0 }; wnd.hInstance = hInstance; wnd.lpszClassName = className; wnd.lpfnWndProc = WndProc; wnd.style = CS_HREDRAW | CS_VREDRAW ; wnd.cbSize = sizeof (WNDCLASSEX); wnd.hIcon = LoadIcon (NULL, IDI_APPLICATION); wnd.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wnd.hCursor = LoadCursor (NULL, IDC_ARROW); wnd.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE ; if (!RegisterClassEx(&wnd)) { FatalAppExit( 0, TEXT("Couldn't register window class!") ); } g_hwnd = CreateWindowEx ( 0, className, TEXT( "Using the system tray" ), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400, NULL, NULL, hInstance, NULL ); // Add the label with instruction text CreateWindow( TEXT("static"), TEXT("right click the system tray icon to close"), WS_CHILD | WS_VISIBLE | SS_CENTER, 0, 0, 400, 400, g_hwnd, 0, hInstance, NULL ) ; // Initialize the NOTIFYICONDATA structure once InitNotifyIconData(); ShowWindow (g_hwnd, iCmdShow); #pragma endregion MSG msg ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Once you get the quit message, before exiting the app, // clean up and remove the tray icon if( !IsWindowVisible( g_hwnd ) ) { Shell_NotifyIcon(NIM_DELETE, &g_notifyIconData); } return msg.wParam; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if ( message==WM_TASKBARCREATED && !IsWindowVisible( g_hwnd ) ) { Minimize(); return 0; } switch (message) { case WM_CREATE: // create the menu once. // oddly, you don't seem to have to explicitly attach // the menu to the HWND at all. This seems so ODD. g_menu = CreatePopupMenu(); AppendMenu(g_menu, MF_STRING, ID_TRAY_EXIT_CONTEXT_MENU_ITEM, TEXT( "Exit" ) ); break; case WM_SYSCOMMAND: switch( wParam & 0xfff0 ) // (filter out reserved lower 4 bits: see msdn remarks http://msdn.microsoft.com/en-us/library/ms646360(VS.85).aspx) { case SC_MINIMIZE: case SC_CLOSE: // redundant to WM_CLOSE, it appears Minimize() ; return 0 ; break; } break; // Our user defined WM_TRAYICON message. // We made this message up, and we told // case WM_TRAYICON: { printf( "Tray icon notification, from %d\n", wParam ) ; switch(wParam) { case ID_TRAY_APP_ICON: printf( "Its the ID_TRAY_APP_ICON.. one app can have several tray icons, ya know..\n" ) ; break; } // the mouse button has been released. // I'd LIKE TO do this on WM_LBUTTONDOWN, it makes // for a more responsive-feeling app but actually // the guy who made the original post is right. // Most apps DO respond to WM_LBUTTONUP, so if you // restore your window on WM_LBUTTONDOWN, then some // other icon will scroll in under your mouse so when // the user releases the mouse, THAT OTHER ICON will // get the WM_LBUTTONUP command and that's quite annoying. if (lParam == WM_LBUTTONUP) { printf( "You have restored me!\n" ) ; Restore(); } else if (lParam == WM_RBUTTONDOWN) // I'm using WM_RBUTTONDOWN here because { printf( "Mmm. Let's get contextual. I'm showing you my context menu.\n" ) ; // it gives the app a more responsive feel. Some apps // DO use this trick as well. Right clicks won't make // the icon disappear, so you don't get any annoying behavior // with this (try it out!) // Get current mouse position. POINT curPoint ; GetCursorPos( &curPoint ) ; // should SetForegroundWindow according // to original poster so the popup shows on top SetForegroundWindow(hwnd); // TrackPopupMenu blocks the app until TrackPopupMenu returns printf("calling track\n"); UINT clicked = TrackPopupMenu( g_menu, TPM_RETURNCMD | TPM_NONOTIFY, // don't send me WM_COMMAND messages about this window, instead return the identifier of the clicked menu item curPoint.x, curPoint.y, 0, hwnd, NULL ); printf("returned from call to track\n"); // Original poster's line of code. Haven't deleted it, // but haven't seen a need for it. //SendMessage(hwnd, WM_NULL, 0, 0); // send benign message to window to make sure the menu goes away. if (clicked == ID_TRAY_EXIT_CONTEXT_MENU_ITEM) { // quit the application. printf("I have posted the quit message, biatch\n"); PostQuitMessage( 0 ) ; } } } break; // intercept the hittest message.. making full body of // window draggable. case WM_NCHITTEST: { // http://www.catch22.net/tuts/tips // this tests if you're on the non client area hit test UINT uHitTest = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam); if(uHitTest == HTCLIENT) return HTCAPTION; else return uHitTest; } case WM_CLOSE: printf( "Got an actual WM_CLOSE Message! Woo hoo!\n" ) ; Minimize() ; return 0; break; case WM_DESTROY: printf( "DESTROY!!\n" ) ; PostQuitMessage (0); break; } return DefWindowProc( hwnd, message, wParam, lParam ) ; }
12 Comments
Thanks for this post. It is useful for me.
thanks. can u post same spell checker code
Try aspell
WM_TASKBARCREATED = RegisterWindowMessageA(“TaskbarCreated”);
You can’t imagine how long I was searching for this tiny piece of code..seriously. Thanks a lot!
thanks…
thanks…
thanks! Appreciate it!
Great example. Really enjoy the posts on this site as they are usually the most complete and well-explained examples I can find on the net.
hfg
// oddly, you don’t seem to have to explicitly attach
// the menu to the HWND at all. This seems so ODD.
You should either free menu resources manually or connect it with HWND via SetMenu(g_hwnd, g_menu);
U stole this from a forum lol
I may have cobbled together my understanding of how to do this from various sources, but the code, presentation and commenting are my own.