应用程序桌面工具栏:Win7/8.1上ABM_SETPOS消息未预留的桌面空间

bio*_*ion 7 mfc appbar windows-7 windows-8.1 windows-10

我正在修改现有的MFC应用程序以使用Win32 应用程序桌面工具栏 API,并且我有一个解决方案似乎大部分都在那里(至少对于Windows 10).但是,有三种行为 - 在三个版本的Windows上 - 我看到并且无法解释:

  1. 在Windows 7上,我的appbar定位代码根本不会移动窗口.进入/退出窗口的appar模式的样式更改正在按预期应用,但窗口保持不变,桌面工作区的大小保持不变.
  2. 在Windows 8上,appbar的位置大部分都符合预期,但最终可能会与任务栏重叠,也不会像我预期的那样在桌面上保留空间.
  3. 在Windows 10上,单个appbar按预期工作,但尝试将两个appbars固定到同一屏幕边缘只会为其中一个保留桌面空间(尽管它们都定位正确).

我的实现代码分为两个主要部分.第一个是进入/退出/修改AppBarState窗口当前的函数,它是一个枚举(包含在一个带有一些辅助函数的类中),包含每个窗口边缘的值以及禁用状态:

static constexpr DWORD AppBarStyleModifications = WS_CAPTION | WS_SIZEBOX | WS_SYSMENU;

if (!AppBarState::IsActive(eState_) && AppBarState::IsActive(eDesiredState)) {
    ModifyStyle(AppBarStyleModifications, 0, SWP_FRAMECHANGED | SWP_NOREDRAW);
    UpdateRegistration_(true);
}

if (AppBarState::IsActive(eDesiredState))
    PositionAppBar_(eDesiredState);

if (AppBarState::IsActive(eState_) && !AppBarState::IsActive(eDesiredState)) {
    UpdateRegistration_(false);
    ModifyStyle(0, AppBarStyleModifications);
}
Run Code Online (Sandbox Code Playgroud)

在此代码中,该UpdateRegistration_函数是一个简单的辅助函数,用于发送ABM_NEWABM_REMOVE消息,视情况而定.实现的主要"肉"是PositionAppBar_,它看起来像这样(有一些健全性检查和错误处理剥离空间):

APPBARDATA Data;
Data.cbSize = sizeof(APPBARDATA);
Data.hWnd = GetSafeHwnd();
Data.uCallbackMessage = APPBAR_CALLBACK;
Data.uEdge = AppBarState::ConvertToPositionCode(eDesiredState);
Data.rc = CRect(GetMonitorInfo(*this).rcMonitor).MulDiv(winx::GetDpiScalingPercent(), 100);

SHAppBarMessage(ABM_QUERYPOS, &Data);

CRect WindowRect;
DwmGetWindowAttribute(Data.hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &WindowRect, sizeof(RECT));
switch (eDesiredState) {
    case AppBarState::PinnedLeft:
        Data.rc.right = Data.rc.left + WindowRect.Width();
        break;
    case AppBarState::PinnedTop:
        Data.rc.bottom = Data.rc.top + WindowRect.Height();
        break;
    case AppBarState::PinnedRight:
        Data.rc.left = Data.rc.right - WindowRect.Width();
        break;
    case AppBarState::PinnedBottom:
        Data.rc.top = Data.rc.bottom - WindowRect.Height();
        break;
}

SHAppBarMessage(ABM_SETPOS, &Data);

const auto TargetRect = CRect(Data.rc).MulDiv(100, winx::GetDpiScalingPercent());
MoveWindow(TargetRect, FALSE);
Run Code Online (Sandbox Code Playgroud)

这里,winx::GetDpiScalingPercent()(顾名思义)是用于确定设备像素和应用坐标之间的DPI缩放比的辅助函数.

虽然欢迎任何关于这里可能存在的问题的意见或建议,但我会尝试用一些更具体的问题来限制范围:

  • 是否有必要ABM_QUERYPOS多次发送消息以获得正确的结果?关于另一个问题的这个答案似乎表明那是(或者至少是)这种情况,但是从我的测试来看,在第二次发送消息后,结果矩形似乎是相同的.
  • 是否存在ABM_SETPOS无法预留空间的有效方案,如果有,在这些方案中可以采取哪些步骤?我在上面的示例代码中省略了这一点,但我正在验证返回值,SHAppBarMessage并确保Data.rc在发送消息后矩形在x轴和y轴上都具有非零大小,所以我希望意味着预订已按预期进行.
  • Windows版本之间的这个API(或其实现)是否存在重大差异,我在这里没有考虑到这一点?除了上面链接的appbar博客文章链接之外,我找不到太多文档,而且在我看来它应该在三个操作系统版本中起到相同的作用,但这并不是因为我的实际行为得到了证实.我看到了.

编辑:快速附录:PositionAppBar_也用作处理ABN_POSCHANGED消息类型的逻辑.