Tomay
Tomay

Reputation: 55

Is sending the message WM_GETTITLEBARINFOEX to get the caption bar RECT stable? or it has a regression?

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.

enter image description here

Here is a zoomed picture:

enter image description here

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:

enter image description here

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

Answers (0)

Related Questions