Skip navigation

! 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 ) ;
}



Download esnips code package (thanks esnips! :)

(donation should be to bill.sherif@gmail.com)

12 Comments

    • Yura
    • Posted May 15, 2009 at 6:56 am
    • Permalink

    Thanks for this post. It is useful for me.

  1. thanks. can u post same spell checker code

  2. Try aspell

    • ds
    • Posted June 27, 2010 at 1:54 pm
    • Permalink

    WM_TASKBARCREATED = RegisterWindowMessageA(“TaskbarCreated”);
    You can’t imagine how long I was searching for this tiny piece of code..seriously. Thanks a lot!

    • Asif Shaikh
    • Posted August 27, 2010 at 12:27 pm
    • Permalink

    thanks…

  3. thanks…

    • Ruslan
    • Posted February 26, 2012 at 7:04 pm
    • Permalink

    thanks! Appreciate it!

    • Rob
    • Posted April 14, 2014 at 4:50 pm
    • Permalink

    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.

    • Anonymous
    • Posted April 17, 2014 at 5:08 am
    • Permalink

    hfg

    • Anonymous
    • Posted November 1, 2014 at 6:57 am
    • Permalink

    // 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);

    • Anonymous
    • Posted October 27, 2019 at 10:49 pm
    • Permalink

    U stole this from a forum lol

  4. I may have cobbled together my understanding of how to do this from various sources, but the code, presentation and commenting are my own.


Leave a comment