我一直关注MSDN's guide to using the DWM API to extend the frame into the client area,但重做了,所以一旦我有了工作,我就可以尝试一下。
但是,当我使用Visual Studio 2013 x64 Native Tools命令行中的cl wincompositiontest.cpp
构建它时,程序不会绘制窗口边框。标准窗口按钮可以工作:我可以看到Windows 7按钮发光和工具提示,然后单击按钮可以执行各自的操作(因此我可以关闭此窗口)。但没有其他工作:我无法移动或调整窗口边缘的大小,边框不会绘制,而是绘制白色:
DwmExtendFrameIntoClientArea()
返回S_OK
。
发生了什么?这是在VirtualBox上运行的Windows 7 x64上;我把代码交给了一个在真实硬件上运行Windows 7的朋友,他们也得到了相同的结果。感谢。
// 12 december 2016
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
// get Windows version right; right now Windows Vista
// unless otherwise stated, all values from Microsoft's sdkddkver.h
// TODO is all of this necessary? how is NTDDI_VERSION used?
// TODO plaform update sp2
#define WINVER 0x0600 /* from Microsoft's winnls.h */
#define _WIN32_WINNT 0x0600
#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */
#define _WIN32_IE 0x0700
#define NTDDI_VERSION 0x06000000
#include <windows.h>
#include <commctrl.h>
#include <uxtheme.h>
#include <windowsx.h>
#include <shobjidl.h>
#include <d2d1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <usp10.h>
#include <msctf.h>
#include <textstor.h>
#include <olectl.h>
#include <shlwapi.h>
#include <dwmapi.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <float.h>
#include <inttypes.h>
#include <vector>
#include <map>
#include <string>
#pragma comment(linker, \
"\"/manifestdependency:type='Win32' " \
"name='Microsoft.Windows.Common-Controls' " \
"version='6.0.0.0' " \
"processorArchitecture='*' " \
"publicKeyToken='6595b64144ccf1df' " \
"language='*'\"")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "uxtheme.lib")
#pragma comment(lib, "dwmapi.lib")
#define HR(call) printf("%s -> 0x%I32X\n", #call, call)
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT r;
MARGINS margins;
BOOL dwmHandled;
LRESULT lResult;
dwmHandled = DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lResult);
switch (uMsg) {
case WM_CREATE:
GetWindowRect(hwnd, &r);
SetWindowPos(hwnd, NULL,
r.left, r.top,
r.right - r.left, r.bottom - r.top,
SWP_FRAMECHANGED);
// TODO if we pass SWP_NOOWNERZORDER || SWP_NOZORDER, the default frame is not correctly inhibited
break;
case WM_ACTIVATE:
margins.cxLeftWidth = 8;
margins.cxRightWidth = 8;
margins.cyBottomHeight = 20;
margins.cyTopHeight = 27;
HR(DwmExtendFrameIntoClientArea(hwnd, &margins));
break;
case WM_NCCALCSIZE:
if (wParam != (WPARAM) FALSE)
return 0;
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
}
if (dwmHandled)
return lResult;
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
int main(void)
{
WNDCLASSW wc;
HWND mainwin;
MSG msg;
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = L"mainwin";
wc.lpfnWndProc = wndproc;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
RegisterClassW(&wc);
mainwin = CreateWindowExW(0,
L"mainwin", L"Main Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
400, 400,
NULL, NULL, GetModuleHandle(NULL), NULL);
ShowWindow(mainwin, SW_SHOWDEFAULT);
UpdateWindow(mainwin);
while (GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
答案 0 :(得分:3)
为了与Windows 10兼容,左/右/下边距应为零。
通常您只需要更改标题栏区域。因此,只需更改margins.cyTopHeight
使用AdjustWindowRectEx
计算给定窗口样式的默认边框宽度,并将这些值传递给WM_NCCALCSIZE
,这将显示默认边框。
DwmDefWindowProc
来响应WM_NCHITTEST
和WM_NCMOUSELEAVE
(但可能您设置它的方式对于以后的兼容性更好)
您还必须处理WM_PAINT
以便系统按钮正确显示。隐藏系统按钮和绘制自己的按钮可能更容易,因此标题栏完全是自定义的,具有自己的背景颜色。
下面是Windows 10的示例,它应该适用于Windows 7.应用程序应该是DPI,否则边界会出现小的显示问题。
void paint_caption(HWND hWnd, HDC hdc, int caption_height)
{
RECT rc;
GetClientRect(hWnd, &rc);
rc.bottom = caption_height;
HDC memdc = CreateCompatibleDC(hdc);
BITMAPINFOHEADER bmpInfoHdr =
{ sizeof(BITMAPINFOHEADER), rc.right, -rc.bottom, 1, 32 };
HBITMAP hbitmap =
CreateDIBSection(memdc, (BITMAPINFO*)(&bmpInfoHdr), DIB_RGB_COLORS, 0, 0, 0);
HGDIOBJ oldbitmap = SelectObject(memdc, hbitmap);
//Note, GDI functions don't support alpha channel, they can't be used here
//Use GDI+, BufferedPaint, or DrawThemeXXX functions
BitBlt(hdc, 0, 0, rc.right, caption_height, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldbitmap);
DeleteObject(hbitmap);
DeleteDC(memdc);
}
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//static MARGINS margins = { -1,-1,100,-1 };
static MARGINS margins = { 0,0,100,0 };
static RECT border_thickness = { 0 };
switch(uMsg)
{
case WM_CREATE:
if(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_THICKFRAME)
{
AdjustWindowRectEx(&border_thickness,
GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
border_thickness.left *= -1;
border_thickness.top *= -1;
}
else if(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER)
{
border_thickness = { 1,1,1,1 };
}
DwmExtendFrameIntoClientArea(hwnd, &margins);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
break;
case WM_NCCALCSIZE:
if(lParam)
{
NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)lParam;
sz->rgrc[0].left += border_thickness.left;
sz->rgrc[0].right -= border_thickness.right;
sz->rgrc[0].bottom -= border_thickness.bottom;
return 0;
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
//paint caption area
paint_caption(hwnd, hdc, margins.cyTopHeight);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCHITTEST:
{
//handle close/minimize/maximize/help button
LRESULT lResult;
if (DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lResult))
return lResult;
//do default processing, except change the result for caption area
lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
if(lResult == HTCLIENT)
{
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
ScreenToClient(hwnd, &pt);
if(pt.y < border_thickness.top) return HTTOP;
if(pt.y < margins.cyTopHeight) return HTCAPTION;
}
return lResult;
}
case WM_NCMOUSELEAVE:
{
LRESULT lResult;
if(DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lResult))
return lResult;
break;
}
case WM_CLOSE:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
Windows 10中的结果:
赢7:
答案 1 :(得分:1)
好吧,所以我调查了一下,尝试将γnράσκωδ'αείπολλάδιδασκόμε和Barmak Shemirani的建议与我最初的(基于MSDN)的建议结合起来,提出似乎可以处理所有案例的事情。一种面向未来的方式。详尽的测试表明,下面的代码完美地处理了Windows 10的奇怪边框鼠标悬停行为(即使是顶部边框,即使在普通窗口中也只触发蓝色边缘,而不是像其他边缘一样略微关闭)。它看起来在Windows 10,Windows 8.1和Windows 7上是正确的。而且,最大化现在也可以正常工作(或者似乎正常工作;我不确定是否存在我缺少的细微差别)!
与Barmak代码的最大区别在于,我从WM_NCCALCSIZE
中提取DefWindowProc()
结果,只是过滤掉了最高结果,让我控制了最高优势,让Windows决定了休息应该是。这也意味着我不需要像Barmak那样跟踪border_thickness
。另外,它清除了我用WM_PAINT注意到的错误,并将窗口的大小调整为重叠到边框中,但我不知道为什么或如何...
defWindowProcFirst
变量控制使用哪种行为。如果将其设置为FALSE
,则会出现前巴马克行为,导致Windows 10出现不一致。
还有一些需要注意的事项:
DefWindowProcW()
的{{1}}返回值,这意味着永远不会触及WM_NCCALCSIZE
,rgrc[1]
和rgrc[2]
,我们可能会错过在一些优化;我需要弄清楚如何处理这些但所有事情都认为这似乎工作正常:)我应该回去测试MSDN代码未经修改但是;我想它会给我类似的结果lppos
虽然......
同时感谢!
defWindowProcFirst = FALSE