3.7. Finding out Button Types and States

Finding out the exact type and state Even for as simple a control as a push button requires some trickery. The reason is that for the most common button types namely radio button and check box among others the class is simply button. As a result, type detection by the class name fails. One work-around is the Win32 API function GetWindowLongPtr that returns the desired window-specific information to the user. In addition to the window handle on which to operate, one must give the function a window specific constant or an index to the window's extra memory area. The window type is stored at the offset given by the constant GWL_STYLE.

GetWindowLongPtr is a relatively new function and using it with Visual Studio 6 requires updating the header and other SDk files at http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ You must also specify the windows version to be greater or equal to 98 (Windows 2000 in the example).

#define _WIN32_WINNT 0x0500
#define WINVER 0x0500

There are several kinds of buttons out there. In addition to the familiar button types push button (BS_PUSHBUTTON), ccheckc box (BS_CHECKBOX) and radio button (BS_RADIOBUTTON) there are groups (BS_GROUPBOX), tri-state checkbox (BS_3STATE) and finally owner drawn buttons (BS_OWNERDRAW and the now legacy BS_USERBUTTON). In addition, the checkable button types have special auto-versions whose checking and unchecking is managed by Windows automatically.

Finding out whether a button is of a given style isn't completely trivial. The style flags for a window are packed into a long style-flag but according to winuser.h, the flags don't always appear to change a single bit in the flags as some of them are mutually exclusive. Possibly the easiest, though not architecture independent way of finding out the style is to get rid of all but the four lowest bits with a bitwise and, and then compare the result against the constants in winuser.h for equality:

DWORD s = GetWindowLongPtr(hWindow, GWL_STYLE) & 0xFL; // All but the 4 LSB.
// ...
if(s == BS_PUSHBUTTON || s == BS_DEFPUSHBUTTON)
{
	isCheckable = FALSE;
	lstrcat(textOut, "push button ");
} // if
// ...
else if(s == BS_CHECKBOX || s == BS_AUTOCHECKBOX)
	lstrcat(textOut, "check box ");
// ...

The state of a checkable button is not unfortunately part of the style flags. In stead, you have to send the button a BM_GETCHECK message to find out the status. The arguments apart from the message type don't seem to matter and the state is indicated in the return value for the sending function. Here we send the message:

switch(SendMessage(hWindow, BM_GETCHECK, 0, 0))
{
	case BST_CHECKED:
		lstrcat(textOut, "selected");
		break;
	case BST_INDETERMINATE:
		lstrcat(textOut, "partially Selected");
		break;
	case BST_UNCHECKED:
		lstrcat(textOut, "unselected");
		break;
} // switch

As the keyboard focus doesn't change when working on a single control, handling HCBT_SETFOCUS for a CBT-hook doesn't guarantee reacting to button state changes. However, on state change a button sends a WM_COMMAND message to its parent window. For controls the lParam is judging by MSDN(Microsoft, 1999, WM_COMMAND) the handle to the sender (e.g. the push button) and the high word of wParam a command id. On pushing a button the id is BN_CLICKED, so checking the button state is:

if(cwp->message == WM_COMMAND && HIWORD(cwp->wParam) == BN_CLICKED)
{ // On button click.
	lstrcat(textOut, "State ");
	handleControls((HWND) cwp->lParam);
	lstrcat(textOut, "\r\n");
	notifyObserver();
} // if
Highword gives the 16 highest bits out of a number. it's opposite is lowword.

Back to the Contents