You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

能否创建仅带边框的WinAPI窗口?透明窗口主体实现方案求助

我来帮你搞定这个仅显示边框、主体全透明的WinAPI窗口问题——之前用blitting的思路确实容易踩坑,试试下面这两个更靠谱的官方方案:

方案1:利用窗口区域(Window Region)实现原生边框透明

这个方案直接让系统只渲染窗口的边框部分,客户区被排除在可见区域外,完全透明,而且保留系统原生的边框样式和交互(比如拖动、最小化)。

核心步骤:

  • 当窗口初始化或大小改变时,计算出窗口四个边框的矩形区域
  • 把这些边框区域合并成一个整体区域,设置为窗口的可见区域
  • 加上WS_EX_TRANSPARENT扩展样式,让鼠标事件能穿透透明的客户区

给你一段可直接测试的代码片段:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_SIZE:
        {
            RECT windowRect, clientRect;
            GetWindowRect(hwnd, &windowRect);
            GetClientRect(hwnd, &clientRect);
            // 把客户区坐标转换为屏幕坐标,方便和窗口整体坐标计算
            ClientToScreen(hwnd, (POINT*)&clientRect.left);
            ClientToScreen(hwnd, (POINT*)&clientRect.right);

            // 创建四个边框的独立区域
            HRGN hRgnTop = CreateRectRgn(windowRect.left, windowRect.top, windowRect.right, clientRect.top);
            HRGN hRgnBottom = CreateRectRgn(windowRect.left, clientRect.bottom, windowRect.right, windowRect.bottom);
            HRGN hRgnLeft = CreateRectRgn(windowRect.left, clientRect.top, clientRect.left, clientRect.bottom);
            HRGN hRgnRight = CreateRectRgn(clientRect.right, clientRect.top, windowRect.right, clientRect.bottom);

            // 合并四个边框区域为一个整体
            HRGN hCombinedRgn = CreateRectRgn(0,0,0,0);
            CombineRgn(hCombinedRgn, hRgnTop, hRgnBottom, RGN_OR);
            CombineRgn(hCombinedRgn, hCombinedRgn, hRgnLeft, RGN_OR);
            CombineRgn(hCombinedRgn, hCombinedRgn, hRgnRight, RGN_OR);

            // 设置窗口的可见区域为合并后的边框区域
            SetWindowRgn(hwnd, hCombinedRgn, TRUE);

            // 释放临时创建的区域资源
            DeleteObject(hRgnTop);
            DeleteObject(hRgnBottom);
            DeleteObject(hRgnLeft);
            DeleteObject(hRgnRight);
            break;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc = {0};
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = WndProc;
    wc.hInstance     = hInstance;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"TransparentBorderWindowClass";
    RegisterClassEx(&wc);

    HWND hwnd = CreateWindowEx(
        WS_EX_TRANSPARENT, // 允许鼠标穿透透明的客户区
        L"TransparentBorderWindowClass",
        L"Transparent Border Window",
        WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, // 带原生边框的窗口样式
        CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
        NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}
方案2:使用分层窗口(Layered Windows)自定义边框

如果需要自定义边框样式(比如改变颜色、粗细),分层窗口是更好的选择。你可以自己绘制边框,然后让客户区部分完全透明。

核心步骤:

  • 创建窗口时添加WS_EX_LAYERED扩展样式
  • 创建一个和窗口大小一致的位图,在上面绘制自定义边框,客户区填充一个指定的透明色
  • 通过UpdateLayeredWindow函数把位图绘制到窗口上,并指定透明色,实现客户区透明

这个方案的优势是可以完全自定义边框的外观,缺点是需要自己处理边框的绘制和窗口大小变化时的重绘逻辑。

注意事项

  • 方案1的优势是完全复用系统原生边框,无需自己绘制,交互逻辑都是系统自带的,稳定性高
  • 如果你之前用blitting没成功,大概率是因为没有正确让系统识别透明区域,上面两个方案都是WinAPI官方支持的透明实现方式,不会有兼容性问题

内容的提问来源于stack exchange,提问作者justin kohlenberg

火山引擎 最新活动