You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建带标题栏可拖拽缩放的中心镂空窗口?

中心镂空录屏窗口的拖拽与缩放功能实现

我的目标是创建一个类似录屏捕获窗口的中心镂空Window。

示例效果

我已通过以下代码实现初步效果:

#include <windows.h>

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);

            // 创建四个细长的矩形作为边框
            HRGN hRgn1 = CreateRectRgn(0, 0, 10, 300);     // 左边框
            HRGN hRgn2 = CreateRectRgn(390, 0, 400, 300); // 右边框
            HRGN hRgn3 = CreateRectRgn(0, 0, 400, 10);     // 上边框
            HRGN hRgn4 = CreateRectRgn(0, 290, 400, 300);  // 下边框

            // 创建空的区域
            HRGN hEmptyRgn = CreateRectRgn(10, 10, 390, 290); // 中间区域

            // 组合区域
            HRGN hCombinedRgn = CreateRectRgn(0, 0, 0, 0);
            CombineRgn(hCombinedRgn, hRgn1, hCombinedRgn, RGN_OR);
            CombineRgn(hCombinedRgn, hRgn2, hCombinedRgn, RGN_OR);
            CombineRgn(hCombinedRgn, hRgn3, hCombinedRgn, RGN_OR);
            CombineRgn(hCombinedRgn, hRgn4, hCombinedRgn, RGN_OR);
            CombineRgn(hCombinedRgn, hEmptyRgn, hCombinedRgn, RGN_DIFF); // 从组合区域中减去中心区域

            // 设置窗口区域
            SetWindowRgn(hwnd, hCombinedRgn);

            // 清理资源
            DeleteObject(hRgn1);
            DeleteObject(hRgn2);
            DeleteObject(hRgn3);
            DeleteObject(hRgn4);
            DeleteObject(hEmptyRgn);
            DeleteObject(hCombinedRgn);

            EndPaint(hwnd, &ps);
            return 0;
        }

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

void CreateHollowWindow() {
    const char CLASS_NAME[] = "HollowWindow";

    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        WS_EX_LAYERED | WS_EX_TOOLWINDOW,
        CLASS_NAME,
        "Hollow Window with Borders",
        WS_POPUP | WS_CAPTION | WS_BORDER,
        CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
        NULL,
        NULL,
        GetModuleHandle(NULL),
        NULL
    );

    // 通过不绘制任何内容使中间镂空
    UpdateWindow(hwnd);
    ShowWindow(hwnd, SW_SHOW);

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

int main() {
    CreateHollowWindow();
    return 0;
}

当前实现效果

当前窗口无标题栏,无法通过拖拽边框进行缩放。请问如何实现正常窗口/对话框的这些功能,仅保留中心镂空?


解决方案

要保留窗口的标题栏拖拽、边框缩放功能,同时维持中心镂空效果,需从窗口样式、区域更新时机、消息处理三个方面调整:

  1. 调整窗口创建参数

    • 移除WS_EX_LAYERED扩展样式,它会干扰标准窗口交互;保留WS_EX_TOOLWINDOW避免在任务栏显示。
    • 窗口主样式改用WS_OVERLAPPEDWINDOW,它自带标题栏、边框、缩放按钮及拖拽缩放功能,替代原有的WS_POPUP | WS_CAPTION | WS_BORDER。
  2. 修改窗口区域设置时机

    • 不在WM_PAINT中重复设置窗口区域(会导致闪烁),改为在窗口创建完成(WM_CREATE)和窗口尺寸变化(WM_SIZE)时更新区域,确保镂空区域随窗口动态调整。
  3. 动态响应窗口缩放

    • 监听WM_SIZE消息,实时重新计算并设置窗口区域,保证边框宽度固定,中心镂空区域随窗口大小同步变化。

调整后的完整代码:

#include <windows.h>

#define BORDER_WIDTH 10

// 计算并设置窗口镂空区域
void UpdateHollowRegion(HWND hwnd) {
    RECT windowRect;
    GetClientRect(hwnd, &windowRect);
    int width = windowRect.right - windowRect.left;
    int height = windowRect.bottom - windowRect.top;

    // 创建四个边框区域
    HRGN hRgnLeft = CreateRectRgn(0, 0, BORDER_WIDTH, height);
    HRGN hRgnRight = CreateRectRgn(width - BORDER_WIDTH, 0, width, height);
    HRGN hRgnTop = CreateRectRgn(BORDER_WIDTH, 0, width - BORDER_WIDTH, BORDER_WIDTH);
    HRGN hRgnBottom = CreateRectRgn(BORDER_WIDTH, height - BORDER_WIDTH, width - BORDER_WIDTH, height);

    // 合并边框区域
    HRGN hCombinedRgn = CreateRectRgn(0, 0, 0, 0);
    CombineRgn(hCombinedRgn, hRgnLeft, hCombinedRgn, RGN_OR);
    CombineRgn(hCombinedRgn, hRgnRight, hCombinedRgn, RGN_OR);
    CombineRgn(hCombinedRgn, hRgnTop, hCombinedRgn, RGN_OR);
    CombineRgn(hCombinedRgn, hRgnBottom, hCombinedRgn, RGN_OR);

    // 设置窗口区域并清理资源
    SetWindowRgn(hwnd, hCombinedRgn, TRUE);
    DeleteObject(hRgnLeft);
    DeleteObject(hRgnRight);
    DeleteObject(hRgnTop);
    DeleteObject(hRgnBottom);
    DeleteObject(hCombinedRgn);
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_CREATE:
            // 窗口创建完成后初始化镂空区域
            UpdateHollowRegion(hwnd);
            return 0;

        case WM_SIZE:
            // 窗口大小改变时更新镂空区域
            UpdateHollowRegion(hwnd);
            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

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

void CreateHollowWindow() {
    const char CLASS_NAME[] = "HollowWindow";

    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        WS_EX_TOOLWINDOW,  // 移除WS_EX_LAYERED,保留工具窗口属性
        CLASS_NAME,
        "Hollow Window with Borders",
        WS_OVERLAPPEDWINDOW,  // 使用标准可缩放窗口样式
        CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
        NULL,
        NULL,
        GetModuleHandle(NULL),
        NULL
    );

    ShowWindow(hwnd, SW_SHOW);

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

int main() {
    CreateHollowWindow();
    return 0;
}

关键说明

  • 窗口样式:WS_OVERLAPPEDWINDOW自带标题栏拖拽、边框缩放功能,无需额外编写拖拽逻辑。
  • 区域更新:在WM_CREATE和WM_SIZE时机设置区域,避免重复创建资源导致的性能问题和界面闪烁。
  • 边框控制:通过宏BORDER_WIDTH统一管理边框粗细,方便后续调整。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:04:51