【发布时间】: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_NCCALCSIZE、WM_NCHITTEST、WM_NCLBUTTONDOWN、WM_NCLBUTTONUP、WM_NCMOUSEMOVE、WM_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 呈现这些按钮。
当前状态(以及到目前为止我尝试过的情况)
我的第一个预感是我需要正确启用视觉样式。
-
Per this article,调用检查操作系统版本将获得 Windows 8除非您通过清单明确针对 Windows 10。 Click here to view my manifest。这确实有效:
- 在 GetVersionEx 返回major=6、minor=2、build=9200 之前
- 现在 GetVersionEx 返回major=10、minor=0、build=10586
-
Per the official "Enabling Visual Styles" article,我确保使用 Common Controls 版本 6。
- 为 Comctl32.lib 添加了链接器输入
- 在程序启动时调用 InitCommonControls
- 将版本 6 的依赖项添加到 application manifest
其他想法
我想尝试的东西已经不多了。在认输之前,我打算尝试一些事情:
-
想法 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 usage 和 Windows 10 Close, Minimize and Maximize buttons)。
-
想法 1 应该可以帮助您进行一些更改。
GetThemeStream请求HTHEME(由GetThemeData返回)。我已经看到将DwmWindow和CompositedWindow::Window作为类列表传递给OpenThemeData的代码。但是,如果有记录,则没有。想法 2 可能不会去任何地方。通常,可以强制一个不可见的窗口呈现自己。 -
这意味着读取“不可能强制一个不可见的窗口呈现自己”.
标签: c winapi visual-c++