Reputation: 55
I used the following code to get the caption bar RECT to do custom NC drawings.
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
#include <tchar.h>
#include <windows.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <dwmapi.h>
// Declare Windows procedure
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
// Make the class name into a global variable
TCHAR szClassName[] = _T("Windows App");
int WINAPI WinMain(HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; // This is the handle for our window
MSG messages; // Here messages to the application are saved
WNDCLASSEX wincl; // Data structure for the windowclass
// The Window structure
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; // This function is called by windows
wincl.style = CS_DBLCLKS; // Catch double-clicks
wincl.cbSize = sizeof(WNDCLASSEX);
// Use default icon and mouse-pointer
wincl.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
wincl.hIconSm = ::LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; // No menu
wincl.cbClsExtra = 0; // No extra bytes after the window class
wincl.cbWndExtra = 0; // structure or the window instance
wincl.hbrBackground = (HBRUSH)COLOR_WINDOW; // Use Windows's default color as the background of the window
// Register the window class, and if it fails quit the program
if (!::RegisterClassEx(&wincl))
return 0;
// The class is registered, let's create the program
hwnd = ::CreateWindowEx(0, // Extended possibilites for variation
szClassName, // Classname
_T("Template Windows App"), // Title Text
WS_OVERLAPPEDWINDOW, // default window
500, // The window left position
300, // The window right position
300, // The programs width
100, // and height in pixels
HWND_DESKTOP, // The window is a child-window to desktop
NULL, // No menu
hThisInstance, // Program Instance handler
NULL // No Window Creation data
);
// Make the window visible on the screen
::ShowWindow(hwnd, nCmdShow);
// Run the message loop. It will run until GetMessage() returns 0
while (::GetMessage(&messages, NULL, 0, 0))
{
// Translate virtual-key messages into character messages
::TranslateMessage(&messages);
// Send message to WindowProcedure
::DispatchMessage(&messages);
}
// The program return-value is 0 - The value that PostQuitMessage() gave
return messages.wParam;
}
// This function is called by the Windows function DispatchMessage()
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// handle the messages
switch (message)
{
case WM_NCCREATE:
{
DWMNCRENDERINGPOLICY ncrp = DWMNCRP_DISABLED;
// Disable non-client area rendering on the window.
::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp));
return TRUE;
}
case WM_NCACTIVATE:
{
return ::DefWindowProc(hwnd, message, wParam, lParam);
//MARGINS margins = {50, 50, 50, 50};
//::DwmExtendFrameIntoClientArea(hwnd, &margins);
::RedrawWindow(hwnd, NULL, NULL, RDW_UPDATENOW /*| RDW_FRAME | RDW_INVALIDATE*/);
}
case WM_NCPAINT:
{
::DefWindowProc(hwnd, message, wParam, lParam);
//#ifndef DCX_NODELETERGN
// #define DCX_NODELETERGN 0x00040000
//#endif
#ifndef DCX_USESTYLE
#define DCX_USESTYLE 0x00010000
#endif
HRGN hrgnClip;
HRGN hrgnCaption;
HRGN hrgnTemp;
RECT rcMin;
RECT rcMax;
RECT rcClose;
RECT rcCaption;
RECT rcIcon;
RECT rcWin;
::GetWindowRect(hwnd, &rcWin);
if (wParam <= 1)
hrgnTemp = ::CreateRectRgnIndirect(&rcWin);
else
hrgnTemp = reinterpret_cast<HRGN>(wParam);
hrgnClip = ::CreateRectRgn(0, 0, 0, 0);
::CombineRgn(hrgnClip, hrgnTemp, NULL, RGN_COPY);
HDC hdc = ::GetDCEx(hwnd, hrgnTemp, DCX_WINDOW | DCX_USESTYLE | DCX_INTERSECTRGN /*| DCX_NODELETERGN*/);
if (!hdc)
break;
TITLEBARINFOEX infoex = { sizeof(infoex) };
::SendMessage(hwnd, WM_GETTITLEBARINFOEX, 0, (LPARAM)&infoex);
TITLEBARINFO info = { sizeof(info) };
::GetTitleBarInfo(hwnd, &info);
rcMin = infoex.rgrect[2];
rcMax = infoex.rgrect[3];
rcClose = infoex.rgrect[5];
rcIcon = infoex.rcTitleBar;
rcIcon.left = rcWin.left + ::GetSystemMetrics(SM_CXFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER) + ::GetSystemMetrics(SM_CXEDGE);
rcIcon.right = rcIcon.left + ::GetSystemMetrics(SM_CXSMICON);
rcCaption = infoex.rcTitleBar;
//rcCaption.left = rcIcon.right + ::GetSystemMetrics(SM_CXEDGE) * 2;
rcCaption.right = rcMin.left - ::GetSystemMetrics(SM_CXEDGE) * 2;
//rcCaption.bottom = rcCaption.top + ::GetSystemMetrics(SM_CYCAPTION);
//rcIcon.bottom = rcCaption.bottom;
hrgnCaption = ::CreateRectRgnIndirect(&rcCaption);
//::ExcludeClipRect(hdc, rcCaption.left, rcCaption.top, rcCaption.right, rcCaption.bottom);
::CombineRgn(hrgnClip, hrgnClip, hrgnCaption, RGN_DIFF);
HBRUSH hbRed = ::CreateSolidBrush(RGB(255, 0, 0));
HBRUSH hbBlue = ::CreateSolidBrush(RGB(0, 0, 255));
::OffsetRgn(hrgnClip, -rcWin.left, -rcWin.top);
::OffsetRect(&rcMin, -rcWin.left, -rcWin.top);
::FrameRect(hdc, &rcMin, hbRed);
::OffsetRect(&rcMax, -rcWin.left, -rcWin.top);
::FrameRect(hdc, &rcMax, hbRed);
::OffsetRect(&rcClose, -rcWin.left, -rcWin.top);
::FrameRect(hdc, &rcClose, hbRed);
::OffsetRect(&rcCaption, -rcWin.left, -rcWin.top);
::FrameRect(hdc, &rcCaption, (HBRUSH)::GetStockObject(BLACK_BRUSH));
::OffsetRect(&rcIcon, -rcWin.left, -rcWin.top);
::FrameRect(hdc, &rcIcon, hbBlue);
//RECT rcFullCaption = rcWin;
//rcFullCaption.bottom = rcFullCaption.top + ::GetSystemMetrics(SM_CYFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER) + ::GetSystemMetrics(SM_CYCAPTION);
//HTHEME hTheme = ::OpenThemeData(hwnd, _T("Window"));
//::OffsetRect(&rcFullCaption, -rcWin.left, -rcWin.top);
//::DrawThemeBackground(hTheme, hdc, WP_CAPTION, CS_ACTIVE, &rcFullCaption, &rcIcon);
//::CloseThemeData(hTheme);
//::FillRect(hdc, &rcFullCaption, (HBRUSH)::GetStockObject(BLACK_BRUSH));
//::RedrawWindow(hwnd, NULL, NULL, RDW_UPDATENOW /*| RDW_FRAME | RDW_INVALIDATE*/);
::DeleteObject(hrgnClip);
::DeleteObject(hrgnCaption);
::DeleteObject(hrgnTemp);
::DeleteObject(hbRed);
::DeleteObject(hbBlue);
::ReleaseDC(hwnd, hdc);
break;
}
case WM_DESTROY:
::PostQuitMessage(0); // send a WM_QUIT to the message queue
break;
// for messages that we don't deal with
default:
return ::DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
So when I called:
TITLEBARINFOEX infoex = { sizeof(infoex) };
::SendMessage(hwnd, WM_GETTITLEBARINFOEX, 0, (LPARAM)&infoex);
then used the resulting infoex.rcTitleBar
I got an incorrect RECT, as illustrated in the following window capture, the rect overlapped the system icon one and is one pixel shorter in the bottom.
Here is a zoomed picture:
But, when I used the old function code instead:
TITLEBARINFO info = { sizeof(info) };
::GetTitleBarInfo(hwnd, &info);
then, everything went back to normal, as illustrated in the following window capture:
So, to get the correct title bar RECT, I either use GetTitleBarInfo()
or calculate its left and bottom sides manually as follows:
rcCaption.left = rcIcon.right + ::GetSystemMetrics(SM_CXEDGE) * 2;
rcCaption.bottom = rcCaption.top + ::GetSystemMetrics(SM_CYCAPTION);
So, you can use the provided code to test it on your side and report the findings back.
TIA.
Used Windows OS: Windows 10 and Windows 11.
Upvotes: 1
Views: 210