在 Windows 10 上使用 DWM API 的 Aero 标题栏问题
Posted
技术标签:
【中文标题】在 Windows 10 上使用 DWM API 的 Aero 标题栏问题【英文标题】:An Aero caption title bar issue using DWM API on the windows 10 【发布时间】:2016-10-13 09:41:07 【问题描述】:为了在标题栏上绘制图标,我参考了this MSDN article 并使用 DWM API 通过调用 DwmExtendFrameIntoClientArea 创建了我的自定义客户区。
我的代码:
CMainFrame::CMainFrame()
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
TRACE0("DWM is enabled\n");
TCHAR szLogoPath[MAX_PATH];
GetModuleFileName ( GetModuleHandle(NULL), szLogoPath, _countof(szLogoPath) );
PathRemoveFileSpec ( szLogoPath );
PathAppend ( szLogoPath, _T("lena.bmp") );
m_pLogoImage = m_pLogoImage->FromFile ( CT2CW(szLogoPath) );
if(NULL == m_pLogoImage)
TRACE0("load image fail\n");
void CMainFrame::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
int xFrame = 2;
int yFrame = 2;
int nTHight = 30;
NCCALCSIZE_PARAMS * p;
RECT * rc;
RECT aRect;
RECT bRect;
RECT acRect;
p = (NCCALCSIZE_PARAMS *)lpncsp;
CopyRect(&bRect,&p->rgrc[1]);
CopyRect(&aRect,&p->rgrc[0]);
acRect.left = aRect.left + xFrame;
acRect.top = aRect.top - nTHight;
acRect.right = aRect.right - xFrame;
acRect.bottom = aRect.bottom - yFrame;
CopyRect(&p->rgrc[0],&acRect);
CopyRect(&p->rgrc[1],&aRect);
CopyRect(&p->rgrc[2],&bRect);
CFrameWnd::OnNcCalcSize(TRUE, lpncsp);
LRESULT CMainFrame::OnNcHitTest(CPoint p)
BOOL dwm_enabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&dwm_enabled)))
LRESULT result = 0;
if (!DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(p.x, p.y), &result))
result = HitTestNCA(m_hWnd, p);
if (result == HTNOWHERE && GetForegroundWindow() != this)
return HTCAPTION;
return result;
return CWnd::OnNcHitTest(p);
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
if(cs.hMenu!=NULL)
::DestroyMenu(cs.hMenu);
cs.hMenu = NULL ;
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.style = WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_OVERLAPPED| WS_SYSMENU | WS_THICKFRAME;
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
cs.lpszClass = AfxRegisterWndClass(0);
return TRUE;
void CMainFrame::OnActivate(UINT nState,CWnd* pWndOther,BOOL bMinimized )
CFrameWnd::OnActivate(nState,pWndOther,bMinimized);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
if(nState == WA_ACTIVE )
MARGINS margins = -1;
/*margins.cyTopHeight = 30;
margins.cxLeftWidth = 0;
margins.cxRightWidth = 0;
margins.cyBottomHeight = 0;*/
HRESULT hr = DwmExtendFrameIntoClientArea(m_hWnd, &margins);
if (!SUCCEEDED(hr))
TRACE0("Failed in DwmExtendFrameIntoClientArea\n");
void CMainFrame::OnNcPaint()
CFrameWnd::OnPaint();
CDC* dc = GetWindowDC();
RECT rcClient;
GetWindowRect(&rcClient);
dc->FillSolidRect(0,0,RECTWIDTH(rcClient),RECTHEIGHT(rcClient),RGB(255,0,0));
CPaintDC gdc(this); // device context for painting
Graphics gr(gdc.m_hDC);
gr.DrawImage ( m_pLogoImage, 0, 0 );
ReleaseDC(dc);
在 Windows 7 下效果很好。
但是,我的窗口在 Windows 10 下出现了另一个未知的标题标题栏。
我发现未知标题是由 cs.style 中的 WS_THICKFRAME 引起的。 如果我删除 WS_THICKFRAME,未知的阳离子栏将消失,但我无法调整窗口边框的大小。此外,我的程序无法再捕获自定义标题栏上的最小值、最大值和关闭按钮消息。 我想删除未知的标题栏而没有任何副作用。 有没有人可以给我一个好的解决方案或建议?
最好的问候,
【问题讨论】:
“有没有人可以给我一个好的解决方案或建议?” - 除非你告诉我们你需要什么,否则这可能不会发生。你说了很多你不想要的东西,但从未对你真正需要的东西做出简洁的描述。 我想删除未知的标题栏,没有任何副作用。谢谢 【参考方案1】:当使用DwmExtendFrameIntoClientArea
时,表示框架扩展到客户区。它不再位于非客户区。所以不需要覆盖OnNcPaint
,你可以在OnPaint
做所有的画
void CMainFrame::OnPaint()
CPaintDC dc(this);
//paint titlebar area (this used to be the non-client area)
CRect rc;
GetClientRect(&rc);
rc.bottom = titlebar_height;
CDC memdc;
memdc.CreateCompatibleDC(&dc);
BITMAPINFOHEADER bmpInfoHeader =
sizeof(BITMAPINFOHEADER), rc.Width(), -rc.Height(), 1, 32 ;
HBITMAP hbitmap = CreateDIBSection(
dc, (BITMAPINFO*)(&bmpInfoHeader), DIB_RGB_COLORS, NULL, NULL, 0);
auto oldbitmap = memdc.SelectObject(hbitmap);
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memdc, 0, 0, SRCCOPY);
memdc.SelectObject(oldbitmap);
DeleteObject(hbitmap);
//begin normal paint
//The new client area begins below titlebar_height which we define earlier
GetClientRect(&rc);
rc.top = titlebar_height;
dc.FillSolidRect(&rc, RGB(0, 0, 255));
Gdiplus::Image *image = Gdiplus::Image::FromFile(L"file.jpg");
Gdiplus::Graphics gr(dc);
gr.DrawImage(image, 0, 0);
delete image;
使用成员变量CRect m_border
来跟踪边框的粗细。您可以使用AdjustWindowRectEx
来查找边框的粗细。
void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
titlebar_height = 100;
//find border thickness
if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_THICKFRAME)
m_border = 0,0,0,0 ;
AdjustWindowRectEx(&m_border, GetWindowLongPtr(m_hWnd,
GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
m_border.left = abs(m_border.left);
m_border.top = abs(m_border.top);
else if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_BORDER)
m_border = 1,1,1,1 ;
else
m_border = 0,0,0,0 ;
//Extend frame in to client area
MARGINS margins = 0 ;
margins.cyTopHeight = titlebar_height; //<<=== *** edited
DwmExtendFrameIntoClientArea(m_hWnd, &margins);
SetWindowPos(NULL, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
m_border
将是例如 7,7,7,7
;
允许 Windows 在左、右、下边框上进行绘制。顶部边框是唯一改变的
void CMainFrame::OnNcCalcSize(BOOL validate, NCCALCSIZE_PARAMS FAR* lpncsp)
if (validate)
lpncsp->rgrc[0].left += m_border.left;
lpncsp->rgrc[0].right -= m_border.right;
lpncsp->rgrc[0].bottom -= m_border.bottom;
else
CFrameWnd::OnNcCalcSize(validate, lpncsp);
另见How to glow the minimum. maximum and close button?
【讨论】:
我试过了,但是我的程序无法捕捉到最小值、最大值和关闭按钮消息。 您之前曾问过类似的问题,并且应该是正确的。我添加了一个完整的示例,请参阅链接。唯一不同的是我添加了一个标题栏具有固定高度的示例,例如 60 像素。但是在您的版本中,您似乎希望整个窗口充当标题栏,在这种情况下将titlebar_height
更改为最大屏幕高度,SystemParametersInfo(SPI_GETWORKAREA....)
不要为OnNcPaint
添加覆盖
@BarmakShemirani,使用此方法时,“绘制的窗口”边框部分透明。一个普通的窗口会有一个 1 像素的实心边框(忽略透明阴影区域)。但这会导致半透明的边框,这会导致 alt-printscreen 的结果看起来很奇怪。想法?以上是关于在 Windows 10 上使用 DWM API 的 Aero 标题栏问题的主要内容,如果未能解决你的问题,请参考以下文章
在 Windows Vista/7 中重定向应用程序的图形输出(使用 DWM)
在具有 DWM 合成的窗口上使用 GDI 绘图时是不是可以防止撕裂伪影?
DWM1000 code 解密10 一 TAG 发送最后一个消息