In the Windows XP login screen, the password text box will warn you with a balloon tooltip if you accidentally turn Caps Lock on:
The balloon tooltip appears to be a Windows tooltip common control with the TTS_BALLOON style.
To replicate this functionality, I decided to write a function called ShowMsgBalloon() which, given a control and the various balloon tooltip parameters, creates and shows the balloon tooltip below the control.
The key insight to making ShowMsgBallon() work as intended was to use the TTF_TRACK option to create a tracking tooltip. This will immediately show the tooltip without requiring the user to position the mouse over the control. The main downside to using TTF_TRACK is that the tooltip will not move with the control if the window is moved; you need to manually move the tooltip using TTM_TRACKPOSITION as required. One could probably make this automatic by subclassing the tooltip’s parent control and handling WM_WINDOWPOSCHANGED messages.
Here is the source code to ShowMsgBalloon(). When you are done with the balloon, call DestroyWindow() on the returned HWND. Note: you may want your application to use comctl32.dll version 6 as it will lead to a nicer visual style, including a close button.
-
#include <windows.h>
-
#include <commctrl.h>
-
-
// Options to ShowMsgBallon() (see dwOpts parameter). These are the
-
// standard icon types for balloon tooltips.
-
#define SMB_ICON_INFO (1 << 0)
-
#define SMB_ICON_WARNING (1 << 1)
-
#define SMB_ICON_ERROR (1 << 2)
-
-
// Given the options passed to ShowMsgBalloon(), determine what
-
// parameter to send to TTM_SETTITLE for the balloon tooltip’s icon.
-
static DWORD
-
GetTitleIcon(DWORD dwOpts)
-
{
-
if (dwOpts & SMB_ICON_INFO)
-
return TTI_INFO;
-
else if (dwOpts & SMB_ICON_WARNING)
-
return TTI_WARNING;
-
else if (dwOpts & SMB_ICON_ERROR)
-
return TTI_ERROR;
-
else
-
return 0;
-
}
-
-
// Create and show a balloon tooltip immediately below the control
-
// hwndCtrl with the given title, message, and options.
-
HWND
-
ShowMsgBalloon(HWND hwndCtrl, LPCTSTR szTitle, LPCTSTR szMsg,
-
DWORD dwOpts)
-
{
-
HWND hwndRet = NULL;
-
HWND hwndTT = NULL;
-
TOOLINFO ti = { 0 };
-
RECT rc;
-
-
// Even though TTS_CLOSE is always specified, a close button will
-
// only be shown if your application has a manifest that requires
-
// comctl32.dll version 6.
-
hwndTT = CreateWindow
-
(
-
TOOLTIPS_CLASS,
-
TEXT(""),
-
WS_POPUP | TTS_NOPREFIX | TTS_BALLOON | TTS_CLOSE,
-
CW_USEDEFAULT, CW_USEDEFAULT,
-
CW_USEDEFAULT, CW_USEDEFAULT,
-
hwndCtrl,
-
NULL,
-
NULL,
-
NULL
-
);
-
if (hwndTT == NULL)
-
goto Cleanup;
-
-
// By using TTTOOLINFO_V1_SIZE rather than sizeof(TOOLINFO),
-
// we don’t require users to be using comctl32 version 6.
-
ti.cbSize = TTTOOLINFO_V1_SIZE;
-
ti.uFlags = TTF_TRACK;
-
ti.hwnd = hwndCtrl;
-
ti.lpszText = const_cast<LPTSTR>(szMsg);
-
if (!SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) &ti))
-
goto Cleanup;
-
if (!SendMessage(hwndTT, TTM_SETTITLE, GetTitleIcon(dwOpts),
-
(LPARAM) szTitle))
-
goto Cleanup;
-
-
// Position the tooltip below the control
-
if (!GetWindowRect(hwndCtrl, &rc))
-
goto Cleanup;
-
SendMessage(hwndTT, TTM_TRACKPOSITION, 0,
-
MAKELONG(rc.left + 10, rc.bottom));
-
-
// Show the tooltip
-
if (!SendMessage(hwndTT, TTM_TRACKACTIVATE, TRUE, (LPARAM) &ti))
-
goto Cleanup;
-
-
hwndRet = hwndTT;
-
hwndTT = NULL;
-
-
Cleanup:
-
if (hwndTT != NULL)
-
::DestroyWindow(hwndTT);
-
-
return hwndRet;
-
}

July 21st, 2008 at 2:22 pm
The code worked fine for me, but I am curious to see how I could add a delegate for when the close button is pressed. I can destroy the window arbitrarily, but would like to set my HWND to null if the close button is pushed. Any idea?
July 21st, 2008 at 9:12 pm
Jared,
Perhaps you can subclass the HWND and capture the WM_CLOSE message.