dwm自绘标题栏

#include <atlbase.h>
#include <atlwin.h>

#include <shellscalingapi.h>
#include <dwmapi.h>

#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "Shcore.lib")

#define GET_X_LPARAM(lParam) ((int)(short)LOWORD(lParam))
#define GET_Y_LPARAM(lParam) ((int)(short)HIWORD(lParam))

const int kTitlebarHeight = 40;

class MainWindow : public CWindowImpl<MainWindow> {
 public:
  BEGIN_MSG_MAP(MainWindow)
  MESSAGE_HANDLER(WM_CREATE, OnCreate)
  MESSAGE_HANDLER(WM_NCCALCSIZE, OnNCCalcSize)
  MESSAGE_HANDLER(WM_NCACTIVATE, OnNCActivate)
      MESSAGE_HANDLER(WM_NCHITTEST, OnNCHitTest)
  MESSAGE_HANDLER(WM_NCPAINT, OnNCPaint)
  MESSAGE_HANDLER(WM_PAINT, OnPaint)
  MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
  MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
  END_MSG_MAP()

  LRESULT OnCreate(UINT uMsg, WPARAM wparam, LPARAM lparam, BOOL&) {
    ATLASSERT(GetStyle() & WS_THICKFRAME);

    return 0;
  }

  LRESULT OnNCCalcSize(UINT uMsg, WPARAM wparam, LPARAM l_param, BOOL&) {
    BOOL mode = wparam;

    RECT* client_rect =
        mode ? &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0])
             : reinterpret_cast<RECT*>(l_param);
    HMONITOR monitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONULL);
    if (!monitor) {
      monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL);
      if (!monitor) {
        return 0;
      }
    }

    RECT insets;
    if (GetClientAreaInsets(monitor, & insets)) {
      client_rect->left += insets.left;
      client_rect->top += insets.top;
      client_rect->bottom -= insets.bottom;
      client_rect->right -= insets.right;
    }
    return 0;
  }
  LRESULT OnNCActivate(UINT uMsg, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    RedrawWindow();
    return 0;
  }

  LRESULT OnNCHitTest(UINT uMsg, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    POINT pt{GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)};
    ::MapWindowPoints(HWND_DESKTOP, m_hWnd, &pt, 1);
    if (pt.y <= kTitlebarHeight) {
      return HTCAPTION;
    }
    return ::DefWindowProc(m_hWnd, WM_NCHITTEST, 0, lparam);
  }

  LRESULT OnNCPaint(UINT uMsg, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    HDC hdc = ::GetWindowDC(m_hWnd);
    if (hdc) {
      RECT rcclient;
      ::GetClientRect(m_hWnd, &rcclient);
      RECT rcwin;
      ::GetWindowRect(m_hWnd, &rcwin);
      POINT ptupleft;
      ptupleft.x = rcwin.left;
      ptupleft.y = rcwin.top;
      ::MapWindowPoints(0, m_hWnd, (LPPOINT)&rcwin,
                        (sizeof(RECT) / sizeof(POINT)));
      ::OffsetRect(&rcclient, -rcwin.left, -rcwin.top);
      ::OffsetRect(&rcwin, -rcwin.left, -rcwin.top);

      HRGN rgntemp = NULL;
      if (wparam == NULLREGION || wparam == ERROR) {
        ::ExcludeClipRect(hdc, rcclient.left, rcclient.top, rcclient.right,
                          rcclient.bottom);
      } else {
        rgntemp = ::CreateRectRgn(
            rcclient.left + ptupleft.x, rcclient.top + ptupleft.y,
            rcclient.right + ptupleft.x, rcclient.bottom + ptupleft.y);
        if (::CombineRgn(rgntemp, (HRGN)wparam, rgntemp, RGN_DIFF) ==
            NULLREGION) {
          // nothing to paint
        }
        ::OffsetRgn(rgntemp, -ptupleft.x, -ptupleft.y);
        ::ExtSelectClipRgn(hdc, rgntemp, RGN_AND);
      }

      ::FillRect(hdc, &rcwin,
                 reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));

      ::ReleaseDC(m_hWnd, hdc);
      if (rgntemp != 0) {
        ::DeleteObject(rgntemp);
      }
    }
    handled = FALSE;
    return 0;
  }

  LRESULT OnPaint(UINT uMsg, WPARAM wparam, LPARAM lparam, BOOL&) {
    PAINTSTRUCT ps;
    HDC display_dc = ::BeginPaint(m_hWnd, &ps);

    if (!IsRectEmpty(&ps.rcPaint)) {
      HBRUSH brush = brush =
          reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
      ::FillRect(ps.hdc, &ps.rcPaint, brush);

      RECT rcclient;
      GetClientRect(&rcclient);
      RECT r{rcclient.left, rcclient.top, rcclient.right,
             rcclient.top + kTitlebarHeight};
      ::FillRect(ps.hdc, &r,
                 reinterpret_cast<HBRUSH>(GetStockObject(GRAY_BRUSH)));
    }

    ::EndPaint(m_hWnd, &ps);
    return 0;
  }

  LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wparam, LPARAM lparam, BOOL&) {
    return 1;
  }

  LRESULT OnWindowPosChanged(UINT uMsg,
                             WPARAM wparam,
                             LPARAM lparam,
                             BOOL& handled) {
    UpdateDwmFrame();
    handled = false;
    return 0;
  }

  bool GetClientAreaInsets(HMONITOR monitor, RECT* insets) const {
    UINT monitor_dpi_x, monitor_dpi_y;
    ::GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &monitor_dpi_x,
                     &monitor_dpi_y);
    const int frame_thickness =
        ::GetSystemMetricsForDpi(SM_CXSIZEFRAME, monitor_dpi_x) +
        ::GetSystemMetricsForDpi(SM_CXPADDEDBORDER, monitor_dpi_x);

    insets->left = frame_thickness;
    insets->top = ::IsZoomed(m_hWnd) ? frame_thickness : 0;
    insets->right = frame_thickness;
    insets->bottom = frame_thickness;
    return true;
  }

  void UpdateDwmFrame() {
    MARGINS margins = {.cxLeftWidth = 0,
                       .cxRightWidth = 0,
                       .cyTopHeight = 0,
                       .cyBottomHeight = 0};

    ::DwmExtendFrameIntoClientArea(m_hWnd, &margins);
  }
};

int main(int argc, char** argv) {
  MainWindow main_window;

  RECT rc{
      .left = 0,
      .top = 0,
      .right = 800,
      .bottom = 600,
  };
  main_window.Create(nullptr, rc, nullptr, WS_OVERLAPPEDWINDOW | WS_THICKFRAME);
  main_window.CenterWindow(::GetDesktopWindow());
  main_window.ShowWindow(SW_NORMAL);

  MSG msg;
  while (::GetMessage(&msg, nullptr, 0, 0)) {
    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);
  }
  return 0;
}
posted @ 2025-08-06 10:36  吱吱的笔记  阅读(57)  评论(0)    收藏  举报