Creating an Access Bar

You first need to decide what you want your access bar to do and what it should look like. I decided that mine should look a bit like the taskbar and that it should contain buttons that let the user change the bar's position (right, left, top, or bottom). To create the access bar itself, I used CreateWindowEx and the extended style WS_EX_TOOLWINDOW:

// Create the access bar.
g_hWndAppBar = CreateWindowEx (WS_EX_TOOLWINDOW,
"AppBarClass", "AppBar",
WS_POPUP | WS_THICKFRAME | WS_CLIPCHILDREN,
0, 0, DEF_APPBAR_WIDTH, DEF_APPBAR_HEIGHT,
NULL, NULL, hInstance, NULL);

// Now create the button children.
g_hwndBtn1 = CreateWindow (
"BUTTON", // create a button
"&Right", // window title
BS_PUSHBUTTON | BS_CENTER | WS_VISIBLE | WS_CHILD,
0, 0, 0, 0,
g_hWndAppBar, // parent window
(HMENU)IDM_RIGHT, // ID
hInstance, // instance
NULL);

g_hwndBtn2 = CreateWindow (
"BUTTON", // create a button
"&Left", // window title
BS_PUSHBUTTON | BS_CENTER | WS_VISIBLE | WS_CHILD,
0, 0, 0, 0,
g_hWndAppBar, // parent window
(HMENU)IDM_LEFT, // ID
hInstance, // instance
NULL);

g_hwndBtn3 = CreateWindow (
"BUTTON", // create a button
"&Top", // window title
BS_PUSHBUTTON | BS_CENTER | WS_VISIBLE | WS_CHILD,
0, 0, 0, 0,
g_hWndAppBar, // parent window
(HMENU)IDM_TOP, // ID
hInstance, // instance
NULL);

g_hwndBtn4 = CreateWindow (
"BUTTON", // create a button
"&Bottom", // window title
BS_PUSHBUTTON | BS_CENTER | WS_VISIBLE | WS_CHILD,
0, 0, 0, 0,
g_hWndAppBar, // parent window
(HMENU)IDM_BOTTOM, // ID
hInstance, // instance
NULL);

Of course, it's not enough to just create the windows and wish upon a star for an access bar—you need to register it with the system. SHELLFUN does this when the user chooses to add the access bar. As I did for the taskbar notification, I filled out a structure, APPBARDATA, and used a special function, SHAppBarMessage, to register the access bar:

case IDM_ADD:
if (! g_fRegistered)
{
// Fill out the structure needed to register the new access bar.
g_appBar.hWnd = g_hWndAppBar;
g_appBar.cbSize = sizeof (APPBARDATA);

// Identifier for notifications
g_appBar.uCallbackMessage = APPBAR_CALLBACK;

// Register the access bar.
if (! SHAppBarMessage (ABM_NEW, &g_appBar))
break;

// Set the default size and position of the access bar.
AppBarPosChanged (ABE_TOP, &g_appBar);

ShowWindow (g_hWndAppBar, SW_SHOW);

// Set the registered flag to TRUE.
g_fRegistered = TRUE;
}
break;

The access bar initially appears anchored to the top of the desktop, as shown in Figure 11-7. Clicking one of the buttons on the bar will change its position.

Figure 11-7.

The access bar in SHELLFUN.

To remove the access bar, you use the same SHAppBarMessage function, specifying the ABM_REMOVE message as the first parameter. In SHELLFUN, I don't actually destroy the window; I hide it in case the user chooses to add it again later. When the application closes, however, I do destroy the window in my cleanup code:

case IDM_REMOVE:
if (g_fRegistered)
{
// Unregister the access bar.
SHAppBarMessage (ABM_REMOVE, &g_appBar);
ShowWindow (g_hWndAppBar, SW_HIDE);
g_fRegistered = FALSE;
}
break;

Because an access bar is a window in its own right, it has its own window procedure, in which events such as activation, creation, and destruction (and lots of other words ending in -tion) happen. This is also where the callback message specified in the ABM_NEW message is sent. Here is the window procedure for SHELLFUN's access bar:

LRESULT CALLBACK AppBarWndProc (HWND hwnd, UINT msg,
WPARAM wparam, 
LPARAM lparam)
{
static HWND hwndBtn1, hwndBtn2, hwndBtn3, hwndBtn4;

switch (msg)
{
case WM_CREATE:
g_fRegistered = FALSE;
break;

case WM_DESTROY:
DestroyWindow (hwndBtn1);
DestroyWindow (hwndBtn2);
DestroyWindow (hwndBtn3);
DestroyWindow (hwndBtn4);
break;

case WM_WINDOWPOSCHANGED:
case WM_ACTIVATE:
{
APPBARDATA abd;
abd.cbSize = sizeof (APPBARDATA);
abd.hWnd = hwnd;
abd.lParam = (LONG)NULL;
SHAppBarMessage (ABM_ACTIVATE, &abd);
break;
}

case WM_COMMAND:
if (HIWORD (wparam) == BN_CLICKED)
AppBarClicked (LOWORD (wparam), (APPBARDATA *)&g_appBar);
break;

case APPBAR_CALLBACK:
AppBarCallback (hwnd, msg, wparam, lparam);
return 0;

default:
return DefWindowProc (hwnd, msg, wparam, lparam);
}
}