// MinimizeToTray // // A couple of routines to show how to make it produce a custom caption // animation to make it look like we are minimizing to and maximizing // from the system tray // // These routines are public domain, but it would be nice if you dropped // me a line if you use them! // // 1.0 29.06.2000 Initial version // 1.1 01.07.2000 The window retains it's place in the Z-order of windows // when minimized/hidden. This means that when restored/shown, it doen't // always appear as the foreground window unless we call SetForegroundWindow // // Copyright 2000 Matthew Ellis #include #include "MinimizeToTray.h" // Odd. VC++6 winuser.h has IDANI_CAPTION defined (as well as IDANI_OPEN and // IDANI_CLOSE), but the Platform SDK only has IDANI_OPEN... // I don't know what IDANI_OPEN or IDANI_CLOSE do. Trying them in this code // produces nothing. Perhaps they were intended for window opening and closing // like the MAC provides... #ifndef IDANI_OPEN #define IDANI_OPEN 1 #endif #ifndef IDANI_CLOSE #define IDANI_CLOSE 2 #endif #ifndef IDANI_CAPTION #define IDANI_CAPTION 3 #endif #define DEFAULT_RECT_WIDTH 150 #define DEFAULT_RECT_HEIGHT 30 // Returns the rect of where we think the system tray is. This will work for // all current versions of the shell. If explorer isn't running, we try our // best to work with a 3rd party shell. If we still can't find anything, we // return a rect in the lower right hand corner of the screen static VOID GetTrayWndRect(LPRECT lpTrayRect) { // First, we'll use a quick hack method. We know that the taskbar is a window // of class Shell_TrayWnd, and the status tray is a child of this of class // TrayNotifyWnd. This provides us a window rect to minimize to. Note, however, // that this is not guaranteed to work on future versions of the shell. If we // use this method, make sure we have a backup! HWND hShellTrayWnd=FindWindowEx(NULL,NULL,TEXT("Shell_TrayWnd"),NULL); if(hShellTrayWnd) { HWND hTrayNotifyWnd=FindWindowEx(hShellTrayWnd,NULL,TEXT("TrayNotifyWnd"),NULL); if(hTrayNotifyWnd) { GetWindowRect(hTrayNotifyWnd,lpTrayRect); return; } } // OK, we failed to get the rect from the quick hack. Either explorer isn't // running or it's a new version of the shell with the window class names // changed (how dare Microsoft change these undocumented class names!) So, we // try to find out what side of the screen the taskbar is connected to. We // know that the system tray is either on the right or the bottom of the // taskbar, so we can make a good guess at where to minimize to APPBARDATA appBarData; appBarData.cbSize=sizeof(appBarData); if(SHAppBarMessage(ABM_GETTASKBARPOS,&appBarData)) { // We know the edge the taskbar is connected to, so guess the rect of the // system tray. Use various fudge factor to make it look good switch(appBarData.uEdge) { case ABE_LEFT: case ABE_RIGHT: // We want to minimize to the bottom of the taskbar lpTrayRect->top=appBarData.rc.bottom-100; lpTrayRect->bottom=appBarData.rc.bottom-16; lpTrayRect->left=appBarData.rc.left; lpTrayRect->right=appBarData.rc.right; break; case ABE_TOP: case ABE_BOTTOM: // We want to minimize to the right of the taskbar lpTrayRect->top=appBarData.rc.top; lpTrayRect->bottom=appBarData.rc.bottom; lpTrayRect->left=appBarData.rc.right-100; lpTrayRect->right=appBarData.rc.right-16; break; } return; } // Blimey, we really aren't in luck. It's possible that a third party shell // is running instead of explorer. This shell might provide support for the // system tray, by providing a Shell_TrayWnd window (which receives the // messages for the icons) So, look for a Shell_TrayWnd window and work out // the rect from that. Remember that explorer's taskbar is the Shell_TrayWnd, // and stretches either the width or the height of the screen. We can't rely // on the 3rd party shell's Shell_TrayWnd doing the same, in fact, we can't // rely on it being any size. The best we can do is just blindly use the // window rect, perhaps limiting the width and height to, say 150 square. // Note that if the 3rd party shell supports the same configuraion as // explorer (the icons hosted in NotifyTrayWnd, which is a child window of // Shell_TrayWnd), we would already have caught it above hShellTrayWnd=FindWindowEx(NULL,NULL,TEXT("Shell_TrayWnd"),NULL); if(hShellTrayWnd) { GetWindowRect(hShellTrayWnd,lpTrayRect); if(lpTrayRect->right-lpTrayRect->left>DEFAULT_RECT_WIDTH) lpTrayRect->left=lpTrayRect->right-DEFAULT_RECT_WIDTH; if(lpTrayRect->bottom-lpTrayRect->top>DEFAULT_RECT_HEIGHT) lpTrayRect->top=lpTrayRect->bottom-DEFAULT_RECT_HEIGHT; return; } // OK. Haven't found a thing. Provide a default rect based on the current work // area SystemParametersInfo(SPI_GETWORKAREA,0,lpTrayRect,0); lpTrayRect->left=lpTrayRect->right-DEFAULT_RECT_WIDTH; lpTrayRect->top=lpTrayRect->bottom-DEFAULT_RECT_HEIGHT; } // Check to see if the animation has been disabled static BOOL GetDoAnimateMinimize(VOID) { ANIMATIONINFO ai; ai.cbSize=sizeof(ai); SystemParametersInfo(SPI_GETANIMATION,sizeof(ai),&ai,0); return ai.iMinAnimate?TRUE:FALSE; } VOID MinimizeWndToTray(HWND hWnd, BOOL animate) { if(GetDoAnimateMinimize() && animate) { RECT rcFrom,rcTo; // Get the rect of the window. It is safe to use the rect of the whole // window - DrawAnimatedRects will only draw the caption GetWindowRect(hWnd,&rcFrom); GetTrayWndRect(&rcTo); // Get the system to draw our animation for us DrawAnimatedRects(hWnd,IDANI_CAPTION,&rcFrom,&rcTo); } // Add the tray icon. If we add it before the call to DrawAnimatedRects, // the taskbar gets erased, but doesn't get redrawn until DAR finishes. // This looks untidy, so call the functions in this order // Hide the window ShowWindow(hWnd,SW_HIDE); } VOID RestoreWndFromTray(HWND hWnd, BOOL animate) { if(GetDoAnimateMinimize() && animate) { // Get the rect of the tray and the window. Note that the window rect // is still valid even though the window is hidden RECT rcFrom,rcTo; GetTrayWndRect(&rcFrom); GetWindowRect(hWnd,&rcTo); // Get the system to draw our animation for us DrawAnimatedRects(hWnd,IDANI_CAPTION,&rcFrom,&rcTo); } // Show the window, and make sure we're the foreground window ShowWindow(hWnd,SW_SHOW); SetActiveWindow(hWnd); SetForegroundWindow(hWnd); // Remove the tray icon. As described above, remove the icon after the // call to DrawAnimatedRects, or the taskbar will not refresh itself // properly until DAR finished }