c++winapinonclient-area

How can I remove a window's non-client area completely?


I need a window featuring no title bar, none of the control boxes, no system menu, and no frames (all that functionality is provided with separate controls).

I suspect that this should be possible to do with CreateWindowExA's window styles argument dwStyle and possibly lpWindowName, as described here: https://learn.microsoft.com/en-us/windows/desktop/winmsg/window-styles

This is how the arguments look like originally:

HWND hwnd = CreateWindowEx(
    0,                              // Optional window styles.
    CLASS_NAME,                     // Window class.
    L"",                            // No window name (title text).
    WS_OVERLAPPEDWINDOW,            // Window style.

    // Size and position.
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

    NULL,       // Parent window.
    NULL,       // Menu.
    hInstance,  // Instance handle.
    NULL        // Additional application data.
);

However, in dwStyle, the normal window style WS_OVERLAPPEDWINDOW is defined as

WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX

with WS_OVERLAPPED being 0x00000000L.

Simply providing 0 and omitting the rest doesn't work, as also the documentation implies: "The window is an overlapped window. An overlapped window has a title bar and a border."

(The funny thing is, I am perfectly able to do this task in VB.NET (and even in VB6) by setting the ControlBox property to False and then by removing the titlebar using Text = "", so I strongly suspect that when possible in VB...)

How would I do my task in C++?


Just in case the WindowProc is needed in order to process a different message, here it is in its minimalistic version:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        case WM_PAINT:
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
            EndPaint(hwnd, &ps);
            return 0;

    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

(Compiling with VS 2017.)


Solution

  • The non-client area of a top-level window can be removed by using only the WS_POPUP style:

    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class.
        L"",                            // No window name (title text).
        WS_POPUP,                       // Window style.
    
        // Size and position.
        100, 100, 400, 300,
    
        NULL,       // Parent window.
        NULL,       // Menu.
        hInstance,  // Instance handle.
        NULL        // Additional application data.
    );
    

    Note that CW_USEDEFAULT for size and position is only valid for overlapped windows. For popup windows you have to be explicit.

    Depending on your use case, the technique described by this answer might be better suitable. Using the DWM API, it allows you to remove the non-client area, but keep the drop shadow to make the window stand out better from the background.