使用应用程序桌面工具栏

应用程序桌面工具栏(也称为应用栏)是类似于 Windows 任务栏的窗口。 它固定在屏幕边缘,通常包含可让用户快速访问其他应用程序和窗口的按钮。 系统会阻止其他应用程序使用应用程序栏使用的桌面区域。 在任何给定时间,桌面上都可以存在任意数量的应用栏。

本主题包含以下各节:

关于应用程序桌面工具栏

Windows 提供了一个 API,可让你利用系统提供的应用栏服务。 这些服务有助于确保应用程序定义的应用栏之间以及与任务栏之间的顺利运行。 系统维护有关每个应用栏的信息,并发送应用栏消息,以通知它们可能影响其大小、位置和外观的事件。

发送消息

应用程序使用一组特殊的消息(称为应用栏消息)添加或删除应用栏、设置应用栏的大小和位置,以及检索有关任务栏的大小、位置和状态的信息。 若要发送应用栏消息,应用程序必须使用 SHAppBarMessage 函数。 函数的参数包括消息标识符,例如 ABM_NEW 以及 APPBARDATA 结构的地址。 结构成员包含系统处理给定消息所需的信息。

对于任何给定的应用栏消息,系统使用 APPBARDATA 结构的一些成员并忽略其他成员。 但是,系统始终使用 cbSizehWnd 成员,因此应用程序必须为每个应用栏消息填充这些成员。 cbSize 成员指定结构的大小,hWnd 成员是应用栏窗口的句柄。

某些应用栏消息从系统请求信息。 处理这些消息时,系统将请求的信息复制到 APPBARDATA 结构中。

登记

系统保留应用栏的内部列表,并维护列表中每个栏的相关信息。 系统使用该信息来管理应用栏、为其执行服务,并向其发送通知消息。

应用程序必须先注册应用栏(即,将其添加到内部列表),然后才能从系统接收应用栏服务。 为了注册应用栏,应用程序会发送 ABM_NEW 消息。 随附的 APPBARDATA 结构包括应用栏窗口的句柄和应用程序定义的消息标识符。 系统使用消息标识符将通知消息发送到应用栏窗口的窗口过程。 有关详细信息,请参阅“应用栏通知消息”。

应用程序通过发送 ABM_REMOVE 消息来取消注册应用栏。 如果取消注册应用栏,会将其从系统的内部应用栏列表中删除。 系统不再向应用栏发送通知消息或阻止其他应用程序使用应用栏使用的屏幕区域。 在销毁应用栏之前,应用程序应始终发送 ABM_REMOVE

应用栏大小和位置

应用程序应设置应用栏的大小和位置,使其不会干扰任何其他应用栏或任务栏。 每个应用栏必须定位到屏幕的特定边缘,多个应用栏可以定位到一个边缘。 但是,如果将应用栏定位到与任务栏相同的边缘,则系统会确保任务栏始终位于最外边缘。

若要设置应用栏的大小和位置,应用程序首先通过发送 ABM_QUERYPOS 消息为应用栏建议屏幕边缘和边框。 系统确定建议矩形内的屏幕区域的任何部分是否由任务栏或其他应用栏使用,调整矩形(如有必要),并将调整后的矩形返回到应用程序。

接下来,应用程序发送 ABM_SETPOS 消息,为应用栏设置新的边框。 同样,系统可以在将矩形返回到应用程序之前调整矩形。 因此,应用程序应使用由 ABM_SETPOS 返回的经过调整的矩形来设置最终的大小和位置。 应用程序可以使用 MoveWindow 函数将应用栏移动到位置。

通过使用两步过程来设置大小和位置,系统使应用程序能够在移动操作期间向用户提供中间反馈。 例如,如果用户拖动应用栏,应用程序可能会显示一个阴影矩形,在应用栏实际移动前指示新位置。

应用程序应在注册应用栏后以及每当应用栏收到 ABN_POSCHANGED 通知消息时设置应用栏的大小和位置。 每当任务栏的大小、位置或可见性状态发生更改以及屏幕同一端的另一个应用栏重设大小、添加或删除时,应用栏都会收到此通知消息。

每当应用栏收到 WM_ACTIVATE 消息时,它都应发送 ABM_ACTIVATE 消息。 同样,当应用栏收到 WM_WINDOWPOSCHANGED 消息时,它必须调用 ABM_WINDOWPOSCHANGED。 发送这些消息可确保系统正确设置同一边缘上任何自动隐藏应用栏的 z 顺序。

自动隐藏应用程序桌面工具栏

自动隐藏应用栏是通常处于隐藏状态但在用户将鼠标光标移动到应用栏与之关联的屏幕边缘时可见的应用栏。 当用户将鼠标光标移出应用栏的边框时,应用栏会再次隐藏自身。

尽管系统允许在任何给定时间使用多个不同的应用栏,但对于每个屏幕边缘,每次只允许一个自动隐藏应用栏,且遵循先到先得的原则。 系统自动维护自动隐藏应用栏的 z 顺序(仅在其 z 顺序组中)。

应用程序使用 ABM_SETAUTOHIDEBAR 消息注册或取消注册自动隐藏应用栏。 消息指定应用栏的边缘,并指定用于指定注册还是取消注册应用栏的标志。 如果注册了自动隐藏应用栏,但已与指定边缘关联,则消息将失败。 应用程序可以通过发送 ABM_GETAUTOHIDEBAR 消息来检索与边缘关联的自动隐藏应用栏的句柄。

自动隐藏应用栏不需要注册为常规应用栏;也就是说,它不需要通过发送 ABM_NEW 消息进行注册。 未由 ABM_NEW 注册的应用栏与定位在屏幕同一边缘的任何应用栏重叠。

应用栏通知消息

系统发送消息,以通知应用栏可能影响其位置和外观的事件。 消息在应用程序定义的消息的上下文中发送。 应用程序在发送 ABM_NEW 消息以注册应用栏时指定消息的标识符。 通知代码位于应用程序定义的消息的 wParam 参数中。

当任务栏的大小、位置或可见性状态发生更改、另一个应用栏添加到屏幕的同一边缘或屏幕同一边缘的另一个应用栏调整大小或删除时,应用栏会收到 ABN_POSCHANGED 通知消息。 应用栏应通过发送 ABM_QUERYPOSABM_SETPOS 消息来响应此通知消息。 如果应用栏的位置已更改,它应调用 MoveWindow 函数将其自身移动到新位置。

每当任务栏的“自动隐藏”或“始终处于顶部”状态更改时,也就是说,当用户在任务栏的属性表中选择或清除始终处于顶部自动隐藏复选框时,系统会发送 ABN_STATECHANGE 通知消息。 应用栏可以使用此通知消息将其状态设置为符合任务栏的状态(如果需要)。

当启动全屏应用程序或关闭最后一个全屏应用程序时,应用栏将收到 ABN_FULLSCREENAPP 通知消息。 lParam 参数指示全屏应用程序是打开还是关闭。 如果打开,应用栏必须下降到 z 顺序的底部。 当最后一个全屏应用程序关闭时,应用栏应还原其 z 顺序位置。

当用户从任务栏的快捷菜单中选择“层叠”、“横向平铺”或“纵向平铺”命令时,应用栏会收到 ABN_WINDOWARRANGE 通知消息。 系统发送消息两次 - 一次是在重新排列窗口前(lParamTRUE),一次是在排列窗口后(lParamFALSE)。

应用栏可以使用 ABN_WINDOWARRANGE 消息从层叠或平铺操作中排除自身。 若要排除自身,应用栏应在 lParamTRUE 时隐藏自身,在 lParamFALSE 时显示自身。 如果应用栏隐藏自身以响应此消息,则无需发送 ABM_QUERYPOSABM_SETPOS 消息以响应层叠或平铺操作。

注册应用程序桌面工具栏

应用程序必须通过发送 ABM_NEW 消息来注册应用栏。 注册应用栏会将其添加到系统的内部列表中,并向系统提供消息标识符,用于向应用栏发送通知消息。 在退出之前,应用程序必须通过发送 ABM_REMOVE 消息来取消注册应用栏。 取消注册会从系统的内部列表中删除应用栏,并阻止应用栏接收应用栏通知消息。

以下示例中的函数注册或取消注册应用栏,具体取决于布尔标志参数的值。

// RegisterAccessBar - registers or unregisters an appbar. 
// Returns TRUE if successful, or FALSE otherwise. 

// hwndAccessBar - handle to the appbar 
// fRegister - register and unregister flag 

// Global variables 
//     g_uSide - screen edge (defaults to ABE_TOP) 
//     g_fAppRegistered - flag indicating whether the bar is registered 

BOOL RegisterAccessBar(HWND hwndAccessBar, BOOL fRegister) 
{ 
    APPBARDATA abd; 
    
    // An application-defined message identifier
    APPBAR_CALLBACK = (WM_USER + 0x01);
    
    // Specify the structure size and handle to the appbar. 
    abd.cbSize = sizeof(APPBARDATA); 
    abd.hWnd = hwndAccessBar; 

    if (fRegister) 
    { 
        // Provide an identifier for notification messages. 
        abd.uCallbackMessage = APPBAR_CALLBACK; 

        // Register the appbar. 
        if (!SHAppBarMessage(ABM_NEW, &abd)) 
            return FALSE; 

        g_uSide = ABE_TOP;       // default edge 
        g_fAppRegistered = TRUE; 

    } 
    else 
    { 
        // Unregister the appbar. 
        SHAppBarMessage(ABM_REMOVE, &abd); 
        g_fAppRegistered = FALSE; 
    } 

    return TRUE; 
}

设置应用栏大小和位置

应用程序应在注册应用栏后、用户移动应用栏或调整应用栏的大小后,以及每当应用栏收到 ABN_POSCHANGED 通知消息时,设置应用栏的大小和位置。 在设置应用栏的大小和位置之前,应用程序通过发送 ABM_QUERYPOS 消息来查询系统以获取批准的边框。 系统返回一个不会干扰任务栏或任何其他应用栏的边框。 系统纯粹通过矩形减法来调整矩形;不会尽力保留矩形的初始大小。 因此,应用栏应在发送 ABM_QUERYPOS 后根据需要重新调整矩形。

接下来,应用程序通过使用 ABM_SETPOS 消息将边框传递回系统。 然后,它调用 MoveWindow 函数将应用栏移动到位置。

以下示例演示如何设置应用栏的大小和位置。

// AppBarQuerySetPos - sets the size and position of an appbar. 

// uEdge - screen edge to which the appbar is to be anchored 
// lprc - current bounding rectangle of the appbar 
// pabd - address of the APPBARDATA structure with the hWnd and cbSize members filled

void PASCAL AppBarQuerySetPos(UINT uEdge, LPRECT lprc, PAPPBARDATA pabd) 
{ 
    int iHeight = 0; 
    int iWidth = 0; 

    pabd->rc = *lprc; 
    pabd->uEdge = uEdge; 

    // Copy the screen coordinates of the appbar's bounding 
    // rectangle into the APPBARDATA structure. 
    if ((uEdge == ABE_LEFT) || (uEdge == ABE_RIGHT)) 
    { 
        iWidth = pabd->rc.right - pabd->rc.left; 
        pabd->rc.top = 0; 
        pabd->rc.bottom = GetSystemMetrics(SM_CYSCREEN); 
    } 
    else 
    { 
        iHeight = pabd->rc.bottom - pabd->rc.top; 
        pabd->rc.left = 0; 
        pabd->rc.right = GetSystemMetrics(SM_CXSCREEN); 
    } 

    // Query the system for an approved size and position. 
    SHAppBarMessage(ABM_QUERYPOS, pabd); 

    // Adjust the rectangle, depending on the edge to which the appbar is anchored.
    switch (uEdge) 
    { 
        case ABE_LEFT: 
            pabd->rc.right = pabd->rc.left + iWidth; 
            break; 

        case ABE_RIGHT: 
            pabd->rc.left = pabd->rc.right - iWidth; 
            break; 

        case ABE_TOP: 
            pabd->rc.bottom = pabd->rc.top + iHeight; 
            break; 

        case ABE_BOTTOM: 
            pabd->rc.top = pabd->rc.bottom - iHeight; 
            break; 
    } 

    // Pass the final bounding rectangle to the system. 
    SHAppBarMessage(ABM_SETPOS, pabd); 

    // Move and size the appbar so that it conforms to the 
    // bounding rectangle passed to the system. 
    MoveWindow(pabd->hWnd, 
               pabd->rc.left, 
               pabd->rc.top, 
               pabd->rc.right - pabd->rc.left, 
               pabd->rc.bottom - pabd->rc.top, 
               TRUE); 

}

处理应用栏通知消息

当任务栏的状态发生更改、全屏应用程序启动(或最后一个关闭)或发生可能影响应用栏大小和位置的事件时,应用栏会收到通知消息。 以下示例演示如何处理各种通知消息。

// AppBarCallback - processes notification messages sent by the system. 

// hwndAccessBar - handle to the appbar 
// uNotifyMsg - identifier of the notification message 
// lParam - message parameter 

void AppBarCallback(HWND hwndAccessBar, UINT uNotifyMsg, 
    LPARAM lParam) 
{ 
    APPBARDATA abd; 
    UINT uState; 

    abd.cbSize = sizeof(abd); 
    abd.hWnd = hwndAccessBar; 

    switch (uNotifyMsg) 
    { 
        case ABN_STATECHANGE: 

            // Check to see if the taskbar's always-on-top state has changed
            // and, if it has, change the appbar's state accordingly.
            uState = SHAppBarMessage(ABM_GETSTATE, &abd); 

            SetWindowPos(hwndAccessBar, 
                         (ABS_ALWAYSONTOP & uState) ? HWND_TOPMOST : HWND_BOTTOM, 
                         0, 0, 0, 0, 
                         SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 

            break; 

        case ABN_FULLSCREENAPP: 

            // A full-screen application has started, or the last full-screen
            // application has closed. Set the appbar's z-order appropriately.
            if (lParam) 
            { 
                SetWindowPos(hwndAccessBar, 
                             (ABS_ALWAYSONTOP & uState) ? HWND_TOPMOST : HWND_BOTTOM, 
                             0, 0, 0, 0, 
                             SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 
            } 
            else 
            { 
                uState = SHAppBarMessage(ABM_GETSTATE, &abd); 

                if (uState & ABS_ALWAYSONTOP) 
                    SetWindowPos(hwndAccessBar, 
                                 HWND_TOPMOST, 
                                 0, 0, 0, 0, 
                                 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 
            } 

        case ABN_POSCHANGED: 

            // The taskbar or another appbar has changed its size or position.
            AppBarPosChanged(&abd); 
            break; 
    } 
}

以下函数调整应用栏的边框,然后调用应用程序定义的 AppBarQuerySetPos 函数(包含在上一部分中),以相应地设置应用栏的大小和位置。

// AppBarPosChanged - adjusts the appbar's size and position. 

// pabd - address of an APPBARDATA structure that contains information 
//        used to adjust the size and position. 

void PASCAL AppBarPosChanged(PAPPBARDATA pabd) 
{ 
    RECT rc; 
    RECT rcWindow; 
    int iHeight; 
    int iWidth; 

    rc.top = 0; 
    rc.left = 0; 
    rc.right = GetSystemMetrics(SM_CXSCREEN); 
    rc.bottom = GetSystemMetrics(SM_CYSCREEN); 

    GetWindowRect(pabd->hWnd, &rcWindow); 

    iHeight = rcWindow.bottom - rcWindow.top; 
    iWidth = rcWindow.right - rcWindow.left; 

    switch (g_uSide) 
    { 
        case ABE_TOP: 
            rc.bottom = rc.top + iHeight; 
            break; 

        case ABE_BOTTOM: 
            rc.top = rc.bottom - iHeight; 
            break; 

        case ABE_LEFT: 
            rc.right = rc.left + iWidth; 
            break; 

        case ABE_RIGHT: 
            rc.left = rc.right - iWidth; 
            break; 
    } 

    AppBarQuerySetPos(g_uSide, &rc, pabd); 
}