Win32 Titlebar How to get the real drawable window size / Remove the Titlebar and Border

我們在 WinProc onPaint() 事件中,可以用下面的方法取得目前 Window Size

// get window's information
BITMAP structBitmapHeader;
memset(&structBitmapHeader, 0, sizeof(BITMAP));

HGDIOBJ hBitmap = GetCurrentObject(hdc, OBJ_BITMAP);
GetObject(hBitmap, sizeof(BITMAP), &structBitmapHeader);

// window's width and height, NOTE: include window title
LONG width = structBitmapHeader.bmWidth;
LONG height = structBitmapHeader.bmHeight;
LONG widthBytes = structBitmapHeader.bmWidthBytes;

// bits per pixel, it should be 24 (3 bytes) or 32 (4 bytes)
bytesPerPixel_ = structBitmapHeader.bmBitsPixel / 8;

但實際上會發現,這裡在 height x width 畫圖時,有些像素會不見。原因是因為這裡的長寬是包括了視窗四個邊的 border 寬度,還有加上 title bar。直覺上,我們應該取得四個 border 和 title bar 的高度,然後在畫圖時提供真實的 Window Size 並在更新畫面時轉換。下面是取的四個邊的 border 和 title bar 的方法。

RECT wrect, crect;
GetWindowRect(hwnd, &wrect);
GetClientRect(hwnd, &crect);

POINT left_top = { crect.left, crect.top };
ClientToScreen(hwnd, &left_top);
POINT right_bottom = { crect.right, crect.bottom };
ClientToScreen(hwnd, &right_bottom);

left_border = lefttop.x - wrect.left;
right_border = wrect.right - rightbottom.x;
bottom_border = wrect.bottom - rightbottom.y;
top_border = lefttop.y - wrect.top;
// real window size is from
// (top_border + bottom_border, 0) to
// (height - 1, width - left_border - right_border - 1 )

這樣我們就算出了真正可以畫圖的視窗的座標了。

但是還有更簡單的方法,就是直接建立一個沒有 title bar 的子視窗就是了。(要直接建立一個沒有 title bar 的母視窗是很麻煩又很複雜的,不要浪費時間做這種事。)

HWND CreateFullscreenWindow(HWND hwnd)
{
    HMONITOR hmon = MonitorFromWindow(hwnd,
        MONITOR_DEFAULTTONEAREST);
    MONITORINFO mi = { sizeof(mi) };
    if (!GetMonitorInfo(hmon, &mi)) return NULL;
    return CreateWindowW(szWindowClass,
        szTitle,
        WS_POPUP | WS_VISIBLE,
        mi.rcMonitor.left,
        mi.rcMonitor.top,
        mi.rcMonitor.right - mi.rcMonitor.left,
        mi.rcMonitor.bottom - mi.rcMonitor.top,
        nullptr, nullptr, hInst, nullptr);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance;
 
   // parent window
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_BORDER | WS_MAXIMIZE,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   // create child windows without titlebar
   HWND hWndChild = CreateFullscreenWindow(hWnd);

   // show child window instead of main window
   ShowWindow(hWndChild, nCmdShow);
   UpdateWindow(hWndChild);

   return TRUE;
}

使用這樣的子視窗,四個 border 都會是 0 所以就不會有任何座標轉換的問題了。如果不想要全視窗,改一下 CreateWindowW 裏面的參數就可以,這樣簡單多了。