Aug 262008

What’s a TopMost window?

A topmost window is one which stays on top of other windows even while it’s not in focus. Normally all application popup windows will move to background once main application window loses focus but a topmost window stays on top, it will not have the input focus though. A good example is our own windows task manager. You can keep it on top to keep an eye on processes that are getting created or for any other purpose for e.g. to keep an eye on process working set.

How to do we setup a TopMost window?

Let me explain or show you the code which does this. Everybody knows how to achieve this but still some don’t, so for them here is how we do this and as always in the form of a function.

void SetTopMost( HWND hWnd, const BOOL TopMost )
   ASSERT( ::IsWindow( hWnd ));
   HWND hWndInsertAfter = ( TopMost ? HWND_TOPMOST : HWND_NOTOPMOST );
   ::SetWindowPos( hWnd, hWndInsertAfter, 0, 0 , 0 , 0, SWP_NOMOVE | SWP_NOSIZE );

The second parameter passed to ::SetWindowPos is the one that does the trick. It’ called hwndInsertAfter, if we specify the after window as HWND_TOPMOST then we get a topmost window, if we specify after window as HWND_NOTOPMOST then topmost setting is unset and our window becomes a normal window.

Equivalent MFC variables for HWND_TOPMOST and HWND_NOTOPMOST are wndTopMost and
wndNoTopMost respectively.

HWND_xxxxx variable are mere #defines just have a look.

#define HWND_TOP        ((HWND)0)
#define HWND_BOTTOM     ((HWND)1)
#define HWND_TOPMOST    ((HWND)-1)
#define HWND_NOTOPMOST  ((HWND)-2)


::SetWindowPos is an interesting function, we can use it

  1. To change the tab order/z-order of a control
  2. To ‘just’ move a control (SWP_MOVE)
  3. To ‘just’ size a control (SWP_SIZE)
  4. To generate ‘just’ nc-paint messages
  5. To generate ‘just’ nc-calcsize messages
  6. To repaint a window

As a homework suggest you to have a look at the flags that are passed to it.

May 302008

Good question! The easiest and working method is to call GetVolumeInformation on a CD-Drive, this function call will fail if no valid volume information is available for a drive this means CD-ROM drive should be valid one too!

So our next job is to find out whether we have a CD-Drive in our machine, for this purpose we iterate through all available drives and use GetDriveType( lpctszDrive ) == DRIVE_CDROM.

As always with me here is a function for this purpose…

bool Is_CD_ROM_Present()
   // Drive buffer
   TCHAR szLogicalDrives[MAX_PATH] = { 0 };

   // Get all drives in our computer, each drive is separated by NULL char
   GetLogicalDriveStrings( MAX_PATH, szLogicalDrives );

   // Value to return, provided to handle multiple CD-ROM drives,
   // if any of them contains a CD-ROM then we return true
   bool RetVal = false;

   // Temp var which helps us in looping through the drive buffer, sicne each drive is
   // NULL separated.
   LPTSTR lptszDrive = szLogicalDrives;
   while( *lptszDrive )
      // Check whether it's a CD-ROM
      if( GetDriveType( lptszDrive ) == DRIVE_CDROM )
         // We don't want a message box to pop up requesting us to insert a CD-ROM,
         // so set error mode accordingly
         const UINT PrevErrMode = SetErrorMode( SEM_FAILCRITICALERRORS );
         const bool RetValTemp = ( GetVolumeInformation( lptszDrive, 0, 0, 0, 0, 0, 0, 0 ) != 0 );

         // To handle multiple CD-ROM drives
         RetVal = RetVal || RetValTemp;

         // Restore error mode
         SetErrorMode( PrevErrMode );
      }// fi

      // Get next drive by moving one char ahead of "NULL" separator between drives
      lptszDrive += _tcslen( lptszDrive ) + 1;
   }// wend

   return RetVal;
}// End Is_CD_ROM_Present
Jan 242008

Recenty a colleague of mine came up and asked if it’s possible to display tooltips for toolbar buttons and other controls without much difficulty. So I gave him the solution and also decided to post it here for anyone who doesn’t know.

When a tooltip text is to be displayed the framework sends a TTN_NEEDTEXT notification, so it’s upto the handler of the event to pass a text to the event. Add two message map entries to your dialog message map section…


Add a function to your your CSomeDlg class…

BOOL CSomeDlg::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
    // OnNeedText should only be called for TTN_NEEDTEXT notifications!
    ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);     // need to handle both ANSI and UNICODE versions of the message

    CString strTipText;    
    UINT nID = pNMHDR->idFrom;

    if ( pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND ) ||
         pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND ))
        // idFrom is actually the HWND of the tool
        nID = ((UINT)(WORD)::GetDlgCtrlID((HWND)nID));

    if (nID != 0) // will be zero on a separator
        strTipText.LoadString( nID );

    // Handle conditionally for both UNICODE and non-UNICODE apps
    #ifndef _UNICODE
        if (pNMHDR->code == TTN_NEEDTEXTA)
            lstrcpyn(pTTTA->szText, strTipText, _countof(pTTTA->szText));
            _mbstowcsz(pTTTW->szText, strTipText, _countof(pTTTW->szText));
        if (pNMHDR->code == TTN_NEEDTEXTA)
            _wcstombsz(pTTTA->szText, strTipText, _countof(pTTTA->szText));
            lstrcpyn(pTTTW->szText, strTipText, _countof(pTTTW->szText));

    *pResult = 0;    

    // Bring tooltip to front
    ::SetWindowPos( pNMHDR->hwndFrom,
                    SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE );    

    return TRUE;    // message was handled

Now tooltips should start appearing…
Well this is not limited just to dialog’s, can be used with any window provided you have provided a string resource corresponding to your control’s id. If you are using visual studio resource editor you have the provision to enter a tooltip text. Don’t forget to call EnableTooltips().

Except for unicode handling, other stuffs are pretty straight forward, easy for you understand. Just in case you are having trouble decrypting 😉 ask me!

A similar sample can be found in MSDN… 😉

Nov 272007

Follow these steps(Just a demo)…

  1. Add two buttons to a fresh dialog in a sequencial order, IDC_BUTTON1 and IDC_BUTTON2
  2. Goto OnInitDialog(you are not restricted to OnInitDialog, you can also do this on a button click) and paste this line of code
GetDlgItem( IDC_BUTTON1 )->SetWindowPos(GetDlgItem( IDC_BUTTON2 ),
                                           0, 0, 0, 0,
                                           SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );

So now run the dialog and you will see that the focus is on the second button i.e. IDC_BUTTON2, I am invoking SetWindowPos on IDC_BUTTON1 and hwndInsertAfter is IDC_BUTTON2 hence IDC_BUTTON1 is moved down in the Z-Order after IDC_BUTTON2. So this is how we dynamically change the Z-Order or tab order.

The key to all this behavior is because SWP_NOZORDER is not given when SetWindowPos is called, so it changes Z-Order of the control on which this method is invoked.

Sep 242007

Here is a function that moves all child windows of a window at one go! 🙂

void MoveChildWindows( HWND hParentWindow, const POINT& ptOffset )
   // Parent window should be valid
   if( !hParentWindow || !::IsWindow( hParentWindow ))
      ASSERT( FALSE );

   // Get first child window
   HWND hWnd = ::GetWindow( hParentWindow, GW_CHILD );
   while( hWnd )
      // Window rectangle
      ::RECT rcRect;
      ::GetWindowRect( hWnd, &rcRect );

      // Offset rectangle
      ::OffsetRect( &rcRect, ptOffset.x, ptOffset.y );

      // Convert to parent co-ordinates from window co-ordinates
      ::MapWindowPoints( ::GetDesktopWindow(), hParentWindow, (LPPOINT)&rcRect, 2 );

      // Move window
      ::SetWindowPos( hWnd,
                      SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE );

      // Get next window which is a sibling of this window
      hWnd = ::GetWindow( hWnd, GW_HWNDNEXT );
   }// End while
}// End MoveChildWindows

How to call:

POINT ptOffset = { -10, -10 };
MoveChildWindows( GetSafeHwnd(), ptOffset ); // Move all child windows left and up
ptOffset.x = 20, ptOffset.y = 20
MoveChildWindows( GetSafeHwnd(), ptOffset ); // Move all child windows right and down
Jun 072007

Use SetWindowPos with SWP_DRAWFRAME.

This won’t generate a WM_PAINT message but instead just a border repaint request, allowing you to just exactly refresh your control’s border only.