【问题标题】:parts drawn with DrawThemeBackground on Windows 10 are not correct在 Windows 10 上使用 DrawThemeBackground 绘制的部分不正确
【发布时间】:2017-01-12 03:59:17
【问题描述】:

问题描述

我想在 C 中创建一个 Windows API 应用程序,它在同一个非客户端区域中呈现菜单和标题按钮,类似于 Firefox

为了做到这一点,我确定解决方案需要:

  • 输入 WS_POPUP,以便菜单与顶部对齐
  • 取得非客户区(呈现菜单的地方)的所有权
  • 手动呈现最小化/最大化/关闭按钮

该解决方案需要在 Windows 7、8 和 10(以及理想的未来版本)上运行。

现在的样子

I have a test program available on GitHub.

在我的应用中,我已经覆盖了相应的事件:

WM_NCCALCSIZEWM_NCHITTESTWM_NCLBUTTONDOWNWM_NCLBUTTONUPWM_NCMOUSEMOVEWM_NCPAINT

然后我在这些事件上重新绘制非客户区域: WM_NCACTIVATE, WM_SETTEXT

这是我如何进行渲染的示例:

// globals set elsewhere
RECT customAreaRect, minRect, maxRect, closeRect, coverMenuRect;
BOOL maximized;

// ...

LRESULT OnPaintNCA(HWND hWnd, WPARAM wParam, LPARAM lParam) {
  RECT windowRect;
  HRGN hRgn = NULL;
  GetWindowRect(hWnd, &windowRect);
  if (wParam == 1) {
    hRgn = CreateRectRgnIndirect(&windowRect);
  } else {
    hRgn = (HRGN)wParam;
  }

  if (hRgn) {
    // Carve out the area for custom content
    HRGN captionButtonRgn = CreateRectRgnIndirect(&customAreaRect);
    CombineRgn(hRgn, hRgn, captionButtonRgn, RGN_XOR);
    DeleteObject(captionButtonRgn);

    // Force default painting for non-client area
    LRESULT ret = DefWindowProc(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);

    // black background covering part of menu, behind buttons
    HDC hDC = GetWindowDC(hWnd);
    FillRect(hDC, &coverMenuRect, (HBRUSH)GetStockObject(BLACK_BRUSH));

    HTHEME hTheme = OpenThemeData(hWnd, TEXT("WINDOW"));

    DrawThemeBackground(hTheme, hDC, WP_MINBUTTON, partState, minRect, NULL);
    DrawThemeBackground(hTheme, hDC, maximized ? WP_RESTOREBUTTON : WP_MAXBUTTON, partState, maxRect, NULL);
    DrawThemeBackground(hTheme, hDC, WP_CLOSEBUTTON, partState, closeRect, NULL);

    CloseThemeData(hTheme);
  }
}

渲染结果如下: 不幸的是,用于部件的样式(最小化、最大化/恢复、关闭)看起来像 Windows 7/8 的样式,而不是原生的 Windows 10 控件。几天来我一直在寻找一种方法来做到这一点,但没有运气。我需要帮助了解如何使用 Windows API 为 Windows 10 呈现这些按钮。

当前状态(以及到目前为止我尝试过的情况)

我的第一个预感是我需要正确启用视觉样式。

以下是我尝试过的相关项目设置的一些截图:

其他想法

我想尝试的东西已经不多了。在认输之前,我打算尝试一些事情:

  • 想法 1:使用GetThemeStream,它允许您检索控件的大小/位图。

    • 像这样加载 aero msstyles 文件:

    HMODULE themeFile = LoadLibraryEx(TEXT("C:\\Windows\\Resources\\Themes\\aero\\aero.msstyles"), NULL, LOAD_LIBRARY_AS_DATAFILE);

    • 像这样(传递加载的主题文件)获取部件的位图(最小化按钮、最大化按钮等):

    GetThemeStream(h, WP_MAXBUTTON, MAXBS_NORMAL, TMT_DISKSTREAM, (void**)&buffer, &bufferSize, themeFile);

    • 加载位图;它似乎是 PNG 格式(我还没到这一步)
    • 绘制位图
  • 想法 2:从具有标题区域(以及最小化、最大化、关闭按钮)的隐藏窗口中复制非客户区。

    • 创建一个包含标题和最小/最大按钮的窗口,从不激活它。
    • 在非客户端绘图中,获取该窗口的 DC 并捕获最小/最大/关闭按钮的像素
    • 使用 bitblt 渲染它们

【问题讨论】:

  • 我们可以看到您创建的清单文件(并链接到您的二进制文件)吗?尽管我没有看到任何迹象,但该主题呈现与应用程序清单有关。您从哪里获得这些信息?
  • 清单文件控制应用程序的许多方面(例如平台兼容性、COM 服务器等)。您之前评论中的链接解释了如何控制 Common Controls 版本 6 的使用。这是使用 Visual Styles 所必需的(但您的清单中缺少它)。还有两种部署清单文件的方法:作为嵌入式资源以及可执行映像旁边的单独文件。如果两者都存在,则嵌入式清单优先。您是否检查过您编写的清单也是被使用的清单?
  • @andlabs:显然,它们仍在主题文件中。这是尚未针对 Windows 8+ 更新的 API(请参阅 GetThemeStream usageWindows 10 Close, Minimize and Maximize buttons)。
  • 想法 1 应该可以帮助您进行一些更改。 GetThemeStream 请求 HTHEME(由 GetThemeData 返回)。我已经看到将DwmWindowCompositedWindow::Window 作为类列表传递给OpenThemeData 的代码。但是,如果有记录,则没有。想法 2 可能不会去任何地方。通常,可以强制一个不可见的窗口呈现自己。
  • 这意味着读取不可能强制一个不可见的窗口呈现自己”.

标签: c winapi visual-c++


【解决方案1】:

我认为问题来自尝试在操作系统版本 >= Win Vista 上使用 WM_NCPAINT。 从 Vista 开始,所有 NC 渲染都由 DWM(桌面窗口管理器)控制。如果你还敢处理WM_NCPAINT,DWM 渲染将被关闭,你会得到“老派”的外观:

From the Shell Revealed Blog:

DWM 没有任何遗留问题,因为应用程序不能 在玻璃框架内绘制,因为它由 a 渲染和管理 完全不同的过程。如果应用程序尝试这样做,Windows 将检测到它并完全移除玻璃框架(因此 恢复到基本框架),以便应用程序可以绘制它 想画画。

要获得正确的结果,您必须执行“DWM way”(特别是“删除标准框架”和“在扩展框架窗口中绘图”部分)。这通过让 DWM 在客户区域内渲染框架来工作,因此您可以在其上进行 绘制。同样使用此解决方案,您不必自己绘制标题按钮。 This answer summarizes the required steps(在“Aero 支持的解决方案”下)。

需要注意的是,您可能必须自己绘制菜单,并且您不能为此使用大多数 GDI API,因为 GDI 会忽略 alpha 通道,如果框架是半透明的(Vista 和 Win 7 默认情况下,Win8+ 带扩展)。 BitBlt() 在源是包含 32bpp 位图和 alpha 通道的内存 DC 时有效。 GDI+ 也可以。

【讨论】:

    猜你喜欢
    • 2012-11-12
    • 2018-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-03
    • 1970-01-01
    • 2018-10-09
    • 2011-08-30
    相关资源
    最近更新 更多