0

What I want is to add a shadow effect to my frameless Window. I'm using CS_DROPSHADOW for the Window Style.

int CALLBACK WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nCmdShow){

    //register window
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_DROPSHADOW; //enable dropshadow
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);

    if (!RegisterClassEx(&wcex))
    {
        MessageBox(NULL,_T("Call to RegisterClassEx failed!"), szTitle, NULL);
        return 1;
    }
    
    hInst = hInstance;
    HWND hWnd = CreateWindow(szWindowClass, szTitle, (WS_POPUP), 0, 0, 450, 500, NULL, NULL, hInstance, NULL); //using WS_POPUP for the sake of CS_DROPSHADOW
    SetWindowLong(hWnd, GWL_STYLE, 0); //Remove all border style

    RECT rc;
    GetWindowRect(hWnd, &rc);
    int xPos = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
    int yPos = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;
    SetWindowPos(hWnd, 0, xPos, yPos, 450, 500, SWP_NOZORDER); //Center the Window

    if (!hWnd){
        MessageBox(NULL, _T("Call to CreateWindow failed!"), szTitle, NULL);
        return 1;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    // Main message loop:
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

The problem is using CS_DROPSHADOW, I only have the effect on BOTTOM|RIGHT of the Window, I want all the four sides to have the same effect. I'm thinking probably possible if I can adjust the offset of it, but I'm not sure.

Originally, the exact output I wanted is a white plain title-bar with only Window Close Button with shadows on the Window Frame, so no caption, no minimize, and no maximize button. I was able to remove the maximize, minimize, and caption but unable to set the color of the title-bar to plain white.

I'm pretty sure this can be done in QT with 4 lines of codes by setting the offset of the shadow effect. Also possible on C# by overriding the CreateParams of the protected form and adding the CS_DROPSHADOW to the class style.

I'm not that familiar with this Win32-API so any help will be very appreciated.

Papilion
  • 87
  • 2
  • 10
  • Your app has no control over how `CS_DROPSHADOW` is rendered, that is up to the OS to decide. If you want a custom drop shadow, then create a custom drop shadow, such as by creating a 2nd dark alpha-blended window and placing it underneath your 1st window. – Remy Lebeau Feb 12 '21 at 02:55
  • `CS_DROPSHADOW` adds the shadow as if the light is coming from the upper-left corner of the window, just like a real shadow appears when the light is not directly from above. If you want something other than that, you need to implement it yourself using a window behind yours that provides the shadow effect. – Ken White Feb 12 '21 at 02:56

1 Answers1

0

I think what you really want is a custom Title Bar which is not that easy. You will need to handle all the functionality for that, for example the drag event.

Luckily we now have this thing called Custom Window Frame Using DWM which you will be able to customize the frame of the Window.

Here's a good example of it, example is base on Console Subsystem, credit to melak47.

And if you want to customize the button, you may check the accepted answered from here.

Putting it all together, here's an example using Windows Subsystem.

BorderlessWindow.hpp

#pragma once

#include <memory>
#include <string>

#include <Windows.h>

struct hwnd_deleter {
    using pointer = HWND;
    auto operator()(HWND handle) const -> void {
        ::DestroyWindow(handle);
    }
};

using unique_handle = std::unique_ptr<HWND, hwnd_deleter>;

class BorderlessWindow {
public:
    BorderlessWindow();
    auto set_borderless(bool enabled) -> void;
    auto set_borderless_shadow(bool enabled) -> void;

private:
    static auto CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept -> LRESULT;
    auto hit_test(POINT cursor) const->LRESULT;

    bool borderless = true; // is the window currently borderless
    bool borderless_resize = false; // should the window allow resizing by dragging the borders while borderless
    bool borderless_drag = true; // should the window allow moving my dragging the client area
    bool borderless_shadow = true; // should the window display a native aero shadow while borderless

    unique_handle handle;
};

Main.cpp

#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 

#include "BorderlessWindow.hpp"

#include <stdexcept>
#include <system_error>

#include <Windows.h>
#include <windowsx.h>
#include <dwmapi.h>
#include <string.h>
#include <tchar.h>
#pragma comment(lib, "dwmapi.lib")

#define ID_EXIT_BUTTON 101
#define ID_MINIMIZED_BUTTON 102

namespace {
    // we cannot just use WS_POPUP style
    // WS_THICKFRAME: without this the window cannot be resized and so aero snap, de-maximizing and minimizing won't work
    // WS_SYSMENU: enables the context menu with the move, close, maximize, minize... commands (shift + right-click on the task bar item)
    // WS_CAPTION: enables aero minimize animation/transition
    // WS_MAXIMIZEBOX, WS_MINIMIZEBOX: enable minimize/maximize
    enum class Style : DWORD {
        windowed = WS_POPUP | WS_CAPTION | WS_BORDER,
        aero_borderless = WS_POPUP | WS_CAPTION | WS_BORDER,
        basic_borderless = WS_POPUP | WS_CAPTION | WS_BORDER
    };

    auto maximized(HWND hwnd) -> bool {
        WINDOWPLACEMENT placement;
        if (!::GetWindowPlacement(hwnd, &placement)) {
            return false;
        }

        return placement.showCmd == false;
    }

    /* Adjust client rect to not spill over monitor edges when maximized.
     * rect(in/out): in: proposed window rect, out: calculated client rect
     * Does nothing if the window is not maximized.
     */
    auto adjust_maximized_client_rect(HWND window, RECT& rect) -> void {
        if (!maximized(window)) {
            return;
        }

        auto monitor = ::MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
        if (!monitor) {
            return;
        }

        MONITORINFO monitor_info{};
        monitor_info.cbSize = sizeof(monitor_info);
        if (!::GetMonitorInfoW(monitor, &monitor_info)) {
            return;
        }

        // when maximized, make the client area fill just the monitor (without task bar) rect,
        // not the whole window rect which extends beyond the monitor.
        rect = monitor_info.rcWork;
    }

    auto last_error(const std::string& message) -> std::system_error {
        return std::system_error(
            std::error_code(::GetLastError(), std::system_category()),
            message
        );
    }

    auto window_class(WNDPROC wndproc) -> const wchar_t* {
        static const wchar_t* window_class_name = [&] {
            WNDCLASSEXW wcx{};
            wcx.cbSize = sizeof(wcx);
            wcx.style = CS_HREDRAW | CS_VREDRAW;
            wcx.hInstance = nullptr;
            wcx.lpfnWndProc = wndproc;
            wcx.lpszClassName = L"TSAWCLASS";
            wcx.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
            wcx.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
            const ATOM result = ::RegisterClassExW(&wcx);
            if (!result) {
                throw last_error("failed to register window class");
            }
            return wcx.lpszClassName;
        }();
        return window_class_name;
    }

    auto composition_enabled() -> bool {
        BOOL composition_enabled = FALSE;
        bool success = ::DwmIsCompositionEnabled(&composition_enabled) == S_OK;
        return composition_enabled && success;
    }

    auto select_borderless_style() -> Style {
        return composition_enabled() ? Style::aero_borderless : Style::basic_borderless;
    }

    auto set_shadow(HWND handle, bool enabled) -> void {
        if (composition_enabled()) {
            static const MARGINS shadow_state[2]{ { 0,0,0,0 },{ 1,1,1,1 } };
            ::DwmExtendFrameIntoClientArea(handle, &shadow_state[enabled]);
        }
    }

    auto create_window(WNDPROC wndproc, void* userdata) -> unique_handle {
        auto handle = CreateWindowExW(
            0, window_class(wndproc), L"Here is the title of the Window, will only show on the taskbar.",
            static_cast<DWORD>(Style::aero_borderless), CW_USEDEFAULT, CW_USEDEFAULT,
            450, 500, nullptr, nullptr, nullptr, userdata
        );

        if (!handle) {
            throw last_error("failed to create window");
        }
        return unique_handle{ handle };
    }
}

BorderlessWindow::BorderlessWindow()
: handle{ create_window(&BorderlessWindow::WndProc, this) }
{
    set_borderless(borderless);
    set_borderless_shadow(borderless_shadow);
    ::ShowWindow(handle.get(), SW_SHOW);
}

void BorderlessWindow::set_borderless(bool enabled) {
    Style new_style = (enabled) ? select_borderless_style() : Style::windowed;
    Style old_style = static_cast<Style>(::GetWindowLongPtrW(handle.get(), GWL_STYLE));

    if (new_style != old_style) {
        borderless = enabled;

        ::SetWindowLongPtrW(handle.get(), GWL_STYLE, static_cast<LONG>(new_style));

        // when switching between borderless and windowed, restore appropriate shadow state
        set_shadow(handle.get(), borderless_shadow && (new_style != Style::windowed));

        //set window to center
        RECT rc;
        GetWindowRect(handle.get(), &rc);
        int xPos = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
        int yPos = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;
        //SetWindowLong(handle.get(), GWL_STYLE, WS_MAXIMIZEBOX);
        SetWindowPos(handle.get(), 0, xPos, yPos, 450, 500, SWP_NOZORDER);

        // redraw frame
        ::SetWindowPos(handle.get(), 0, xPos, yPos, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
        ::ShowWindow(handle.get(), SW_SHOW);
    }
}

void BorderlessWindow::set_borderless_shadow(bool enabled) {
    if (borderless) {
        borderless_shadow = enabled;
        set_shadow(handle.get(), enabled);
    }
}

HFONT CreateTitleBarButton(HWND hwnd) {
    static HFONT s_hFont = NULL;
    const TCHAR* fontName = _T("Calibri (Body)"); //you can set custom font name here.
    const long nFontSize = 18;

    HDC hdc = GetDC(hwnd);

    LOGFONT logFont = { 0 };
    logFont.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
    logFont.lfWeight = FW_REGULAR;
    _tcscpy_s(logFont.lfFaceName, fontName);

    s_hFont = CreateFontIndirect(&logFont);

    ReleaseDC(hwnd, hdc);
    HWND btn_minimized = CreateWindowEx(NULL, TEXT("button"), TEXT("\u2212"), 
        WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        379, -1, 35, 28, hwnd, (HMENU)ID_MINIMIZED_BUTTON, NULL, NULL);

    HWND btn_exit = CreateWindowEx(NULL, TEXT("button"), TEXT("\u00D7"),
        WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        415, -1, 35, 28, hwnd, (HMENU)ID_EXIT_BUTTON, NULL, NULL);

    SendMessage(btn_exit, WM_SETFONT, (WPARAM)s_hFont, (LPARAM)MAKELONG(TRUE, 0));
    SendMessage(btn_minimized, WM_SETFONT, (WPARAM)s_hFont, (LPARAM)MAKELONG(TRUE, 0));
    return s_hFont;
}

auto CALLBACK BorderlessWindow::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept -> LRESULT {
    static HFONT FONT_TITLEBAR = NULL;
    if (msg == WM_NCCREATE) {
        auto userdata = reinterpret_cast<CREATESTRUCTW*>(lparam)->lpCreateParams;
        // store window instance pointer in window user data
        ::SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(userdata));
    }
   
    if (auto window_ptr = reinterpret_cast<BorderlessWindow*>(::GetWindowLongPtrW(hwnd, GWLP_USERDATA))) {
        auto& window = *window_ptr;

        switch (msg) {
        case WM_NCCALCSIZE: {
            if (wparam == TRUE && window.borderless) {
                auto& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(lparam);
                adjust_maximized_client_rect(hwnd, params.rgrc[0]);
                return 0;
            }
            break;
        }
        case WM_NCHITTEST: {
            // When we have no border or title bar, we need to perform our
            // own hit testing to allow resizing and moving.
            if (window.borderless) {
                return window.hit_test(POINT{
                    GET_X_LPARAM(lparam),
                    GET_Y_LPARAM(lparam)
                    });
            }
            break;
        }
        case WM_NCACTIVATE: {
            if (!composition_enabled()) {
                // Prevents window frame reappearing on window activation
                // in "basic" theme, where no aero shadow is present.
                return 1;
            }
            break;
        }
        case WM_PAINT: {

            PAINTSTRUCT ps;
            HDC hdc;
            TCHAR greeting[] = _T("Hello, Windows desktop!");
            hdc = BeginPaint(hwnd, &ps);
            TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
            EndPaint(hwnd, &ps);

            break;
        }
        case WM_CREATE: {
            FONT_TITLEBAR = CreateTitleBarButton(hwnd);
            break;
        }
        case WM_NOTIFY: {
            static HBRUSH BRUSH_BUTTON_MINIMIZED_NORMAL = NULL;
            static HBRUSH BRUSH_BUTTON_MINIMIZED_HOVER = NULL;
            static HBRUSH BRUSH_BUTTON_MINIMIZED_CLICKED = NULL;

            static HBRUSH BRUSH_BUTTON_EXIT_NORMAL = NULL;
            static HBRUSH BRUSH_BUTTON_EXIT_HOVER = NULL;
            static HBRUSH BRUSH_BUTTON_EXIT_CLICKED = NULL;

            LPNMHDR some_item = (LPNMHDR)lparam;
            if (some_item->idFrom == ID_EXIT_BUTTON && some_item->code == NM_CUSTOMDRAW) {
                LPNMCUSTOMDRAW item = (LPNMCUSTOMDRAW)some_item;
                if (item->uItemState & CDIS_SELECTED) {
                    if (BRUSH_BUTTON_EXIT_CLICKED == NULL)
                        BRUSH_BUTTON_EXIT_CLICKED = CreateSolidBrush(RGB(227, 61, 61));

                    //Select our color when the button is selected
                    if (BRUSH_BUTTON_EXIT_CLICKED == NULL)
                        BRUSH_BUTTON_EXIT_CLICKED = CreateSolidBrush(RGB(227, 61, 61));
                    //Create pen for button border
                    HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(227, 61, 61));
                    //Select our brush into hDC
                    HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                    HGDIOBJ old_brush = SelectObject(item->hdc, BRUSH_BUTTON_EXIT_CLICKED);

                    //If you want rounded button, then use this, otherwise use FillRect().
                    RoundRect(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom, 0, 0);

                    //Clean up
                    SelectObject(item->hdc, old_pen);
                    SelectObject(item->hdc, old_brush);
                    DeleteObject(pen);

                    //Now, I don't want to do anything else myself (draw text) so I use this value for return:
                    return CDRF_DODEFAULT;
                } else {
                    if (item->uItemState & CDIS_HOT) //Our mouse is over the button
                    {
                        if (BRUSH_BUTTON_EXIT_HOVER == NULL)
                            BRUSH_BUTTON_EXIT_HOVER = CreateSolidBrush(RGB(227, 61, 61));

                        HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(227, 61, 61));
                        HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                        HGDIOBJ old_brush = SelectObject(item->hdc, BRUSH_BUTTON_EXIT_HOVER);
                        RoundRect(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom, 0, 0);
                        SelectObject(item->hdc, old_pen);
                        SelectObject(item->hdc, old_brush);
                        DeleteObject(pen);
                        return CDRF_DODEFAULT;
                    }

                    //Select our color when our button is doing nothing
                    if (BRUSH_BUTTON_EXIT_NORMAL == NULL)
                        BRUSH_BUTTON_EXIT_NORMAL = CreateSolidBrush(RGB(255, 255, 255));

                    HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(255, 255, 255));
                    HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                    HGDIOBJ old_brush = SelectObject(item->hdc, BRUSH_BUTTON_EXIT_NORMAL);
                    RoundRect(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom, 0, 0);
                    SelectObject(item->hdc, old_pen);
                    SelectObject(item->hdc, old_brush);
                    DeleteObject(pen);

                    return CDRF_DODEFAULT;
                }
            }
            else if (some_item->idFrom == ID_MINIMIZED_BUTTON && some_item->code == NM_CUSTOMDRAW) {
                LPNMCUSTOMDRAW item = (LPNMCUSTOMDRAW)some_item;
                if (item->uItemState & CDIS_SELECTED) {
                    if (BRUSH_BUTTON_MINIMIZED_CLICKED == NULL)
                        BRUSH_BUTTON_MINIMIZED_CLICKED = CreateSolidBrush(RGB(232, 232, 232));//CreateGradientBrush(RGB(180, 0, 0), RGB(255, 180, 0), item);

                    //Select our color when the button is selected
                    if (BRUSH_BUTTON_MINIMIZED_CLICKED == NULL)
                        BRUSH_BUTTON_MINIMIZED_CLICKED = CreateSolidBrush(RGB(232, 232, 232));
                    //Create pen for button border
                    HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(232, 232, 232));
                    //Select our brush into hDC
                    HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                    HGDIOBJ old_brush = SelectObject(item->hdc, BRUSH_BUTTON_MINIMIZED_CLICKED);

                    //If you want rounded button, then use this, otherwise use FillRect().
                    RoundRect(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom, 0, 0);

                    //Clean up
                    SelectObject(item->hdc, old_pen);
                    SelectObject(item->hdc, old_brush);
                    DeleteObject(pen);

                    //Now, I don't want to do anything else myself (draw text) so I use this value for return:
                    return CDRF_DODEFAULT;
                }
                else {
                    if (item->uItemState & CDIS_HOT) //Our mouse is over the button
                    {
                        if (BRUSH_BUTTON_MINIMIZED_HOVER == NULL)
                            BRUSH_BUTTON_MINIMIZED_HOVER = CreateSolidBrush(RGB(214, 214, 214));

                        HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(214, 214, 214));
                        HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                        HGDIOBJ old_brush = SelectObject(item->hdc, BRUSH_BUTTON_MINIMIZED_HOVER);
                        RoundRect(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom, 0, 0);
                        SelectObject(item->hdc, old_pen);
                        SelectObject(item->hdc, old_brush);
                        DeleteObject(pen);
                        return CDRF_DODEFAULT;
                    }

                    //Select our color when our button is doing nothing
                    if (BRUSH_BUTTON_MINIMIZED_NORMAL == NULL)
                        BRUSH_BUTTON_MINIMIZED_NORMAL = CreateSolidBrush(RGB(255, 255, 255));

                    HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(255, 255, 255));
                    HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                    HGDIOBJ old_brush = SelectObject(item->hdc, BRUSH_BUTTON_MINIMIZED_NORMAL);
                    RoundRect(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom, 0, 0);
                    SelectObject(item->hdc, old_pen);
                    SelectObject(item->hdc, old_brush);
                    DeleteObject(pen);

                    return CDRF_DODEFAULT;
                }
            }
            break;
        }
        case WM_COMMAND: {
            if (LOWORD(wparam) == ID_MINIMIZED_BUTTON) {
                ShowWindow(hwnd, SW_MINIMIZE);
            }else if (LOWORD(wparam) == ID_EXIT_BUTTON) {
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            }
            break;
        }
        case WM_CLOSE: {
            ::DestroyWindow(hwnd);
            return 0;
        }

        case WM_DESTROY: {
            DeleteObject(FONT_TITLEBAR);
            PostQuitMessage(0);
            return 0;
        }

        case WM_KEYDOWN:
        case WM_SYSKEYDOWN: {
            switch (wparam) {
            case VK_F8: { window.borderless_drag = !window.borderless_drag;        return 0; }
            case VK_F9: { window.borderless_resize = !window.borderless_resize;    return 0; }
            case VK_F10: { window.set_borderless(!window.borderless);               return 0; }
            case VK_F11: { window.set_borderless_shadow(!window.borderless_shadow); return 0; }
            }
            break;
        }
        }
    }

    return ::DefWindowProcW(hwnd, msg, wparam, lparam);
}

auto BorderlessWindow::hit_test(POINT cursor) const -> LRESULT {
    // identify borders and corners to allow resizing the window.
    // Note: On Windows 10, windows behave differently and
    // allow resizing outside the visible window frame.
    // This implementation does not replicate that behavior.
    const POINT border{
        ::GetSystemMetrics(SM_CXFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER),
        ::GetSystemMetrics(SM_CYFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER)
    };
    RECT window;
    if (!::GetWindowRect(handle.get(), &window)) {
        return HTNOWHERE;
    }

    const auto drag = borderless_drag ? HTCAPTION : HTCLIENT;

    enum region_mask {
        client = 0b0000,
        left = 0b0001,
        right = 0b0010,
        top = 0b0100,
        bottom = 0b1000,
    };

    const auto result =
        left * (cursor.x < (window.left + border.x)) |
        right * (cursor.x >= (window.right - border.x)) |
        top * (cursor.y < (window.top + border.y)) |
        bottom * (cursor.y >= (window.bottom - border.y));

    switch (result) {
    case left: return borderless_resize ? HTLEFT : drag;
    case right: return borderless_resize ? HTRIGHT : drag;
    case top: return borderless_resize ? HTTOP : drag;
    case bottom: return borderless_resize ? HTBOTTOM : drag;
    case top | left: return borderless_resize ? HTTOPLEFT : drag;
    case top | right: return borderless_resize ? HTTOPRIGHT : drag;
    case bottom | left: return borderless_resize ? HTBOTTOMLEFT : drag;
    case bottom | right: return borderless_resize ? HTBOTTOMRIGHT : drag;
    case client: return drag;
    default: return HTNOWHERE;
    }
}

int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
    try {
        BorderlessWindow window;
        MSG msg;
        while (::GetMessageW(&msg, nullptr, 0, 0) == TRUE) {
            ::TranslateMessage(&msg);
            ::DispatchMessageW(&msg);
        }
    }
    catch (const std::exception& e) {
        ::MessageBoxA(nullptr, e.what(), "Unhandled Exception", MB_OK | MB_ICONERROR);
    }
}

You can improve the code by cleaning and removing what you don't need. I won't be able to explain everything written into that code but you may check the comments for clarification. Also, read the documentation of Custom Window Frame Using DWM.

Polar
  • 3,327
  • 4
  • 42
  • 77
  • What's with Win32API? This should be easy to do. – Papilion Feb 16 '21 at 08:43
  • I managed to make it work and cleaned it up by removing some of the unnecessary code, but I still want to adjust the offset of CS_DROPSHADOW if possible. Anyway, I'm aware that I'll have to handle everything such as dragging events and your code completed it. Thanks! – Papilion Feb 17 '21 at 01:00
  • adjust_maximized_client_rect works fine when i double click on non client area or minimize/restore button within window, **but** when window was maximized and minimized by **taskbar** and then restored it still fit wrong size of my montor. Any idea how fix this? – Kenny May 03 '23 at 07:11