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

如何防止C++屏幕绘图程序中绘制的像素消失?

问题原因

你遇到的像素消失问题,本质是Windows的窗口重绘机制导致的:当屏幕区域被其他窗口覆盖再移开时,系统会要求该区域所属的窗口重新绘制内容,但你的程序只是通过SetPixel临时在屏幕DC上画像素,既没有保存所有绘制的像素信息,也没有处理重绘逻辑,所以之前画的内容会被系统清除。

解决方案

要解决这个问题,你需要:

  • 持久化存储所有绘制的像素数据:不仅记录坐标,还要记录每个坐标对应的颜色和笔触大小,确保重绘时能完整还原内容。
  • 创建自定义绘图窗口:用一个全屏透明的顶层窗口承载绘图内容,响应系统的WM_PAINT重绘消息,在需要时重新绘制所有保存的像素。
  • 用窗口消息替代全局钩子:窗口可以直接处理鼠标和键盘事件,逻辑更简洁,无需额外的全局钩子。
修改后的完整代码
#include <Windows.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>

// 存储每个绘制像素的坐标、颜色和笔触大小
struct DrawPixel {
    int x;
    int y;
    COLORREF color;
    int penSize; // 0=细笔,1=中粗笔,2=粗笔
};

std::vector<DrawPixel> drawHistory; // 所有绘制的像素历史记录
COLORREF currentColor = RGB(255, 255, 255); // 当前绘图颜色
int currentPenSize = 0; // 默认细笔
bool isDrawing = false; // 是否处于绘图状态(左键按下)

// 写入坐标到文件(保留原有逻辑)
void WriteCoordinateToFile(int x, int y) {
    if (x >= 0 && x <= GetSystemMetrics(SM_CXSCREEN) && y >= 0 && y <= GetSystemMetrics(SM_CYSCREEN)) {
        std::ofstream outputFile("coordinates.txt", std::ios::app);
        if (outputFile.is_open()) {
            outputFile << x << " " << y << std::endl;
            outputFile.close();
        } else {
            std::cerr << "无法打开文件!" << std::endl;
        }
    }
}

// 根据笔触大小绘制区域
void DrawPen(HDC hdc, int x, int y, COLORREF color, int penSize) {
    switch (penSize) {
        case 2: // 粗笔(x键)
            for (int i = -10; i <= 10; ++i) {
                for (int j = -10; j <= 10; ++j) {
                    SetPixel(hdc, x + i, y + j, color);
                }
            }
            break;
        case 1: // 中粗笔(t键)
            for (int i = -2; i <= 2; ++i) {
                for (int j = -2; j <= 2; ++j) {
                    SetPixel(hdc, x + i, y + j, color);
                }
            }
            break;
        default: // 细笔
            SetPixel(hdc, x, y, color);
            break;
    }
}

// 窗口消息处理函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_MOUSEMOVE: {
            if (!isDrawing) break;
            int x = LOWORD(lParam);
            int y = HIWORD(lParam);
            POINT pt = {x, y};
            ClientToScreen(hwnd, &pt);

            HDC hdc = GetDC(hwnd);
            DrawPen(hdc, x, y, currentColor, currentPenSize);
            ReleaseDC(hwnd, hdc);

            // 保存绘制记录
            drawHistory.push_back({x, y, currentColor, currentPenSize});
            WriteCoordinateToFile(pt.x, pt.y);
            break;
        }
        case WM_LBUTTONDOWN: {
            isDrawing = true;
            SetCapture(hwnd); // 捕获鼠标,移出窗口也能继续绘图
            break;
        }
        case WM_LBUTTONUP: {
            isDrawing = false;
            ReleaseCapture();
            break;
        }
        case WM_KEYDOWN: {
            switch (wParam) {
                case 't': case 'T':
                    currentPenSize = currentPenSize == 1 ? 0 : 1;
                    std::cout << (currentPenSize == 1 ? "开启中粗笔" : "切换为细笔") << std::endl;
                    break;
                case 'x': case 'X':
                    currentPenSize = currentPenSize == 2 ? 0 : 2;
                    std::cout << (currentPenSize == 2 ? "开启粗笔" : "切换为细笔") << std::endl;
                    break;
                case 'g':
                    currentColor = RGB(0, 255, 0);
                    std::cout << "已选择绿色" << std::endl;
                    break;
                case 'G':
                    currentColor = RGB(0, 76, 0);
                    std::cout << "已选择深绿色" << std::endl;
                    break;
                case 'r':
                    currentColor = RGB(255, 0, 0);
                    std::cout << "已选择红色" << std::endl;
                    break;
                case 'R':
                    currentColor = RGB(76, 0, 0);
                    std::cout << "已选择深红色" << std::endl;
                    break;
                case 'b':
                    currentColor = RGB(0, 0, 255);
                    std::cout << "已选择蓝色" << std::endl;
                    break;
                case 'B':
                    currentColor = RGB(0, 0, 76);
                    std::cout << "已选择深蓝色" << std::endl;
                    break;
                default:
                    currentColor = RGB(255, 255, 255);
                    std::cout << "未知按键,切换为默认白色" << std::endl;
                    break;
            }
            break;
        }
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            // 重绘所有历史记录
            for (const auto& pixel : drawHistory) {
                DrawPen(hdc, pixel.x, pixel.y, pixel.color, pixel.penSize);
            }
            EndPaint(hwnd, &ps);
            break;
        }
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

int main() {
    // 注册窗口类
    const wchar_t CLASS_NAME[] = L"ScreenDrawClass";
    WNDCLASS wc = {0};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); // 透明背景
    wc.style = CS_HREDRAW | CS_VREDRAW;

    RegisterClass(&wc);

    // 创建全屏透明顶层窗口
    HWND hwnd = CreateWindowEx(
        WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
        CLASS_NAME,
        L"屏幕绘图",
        WS_POPUP,
        0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
        NULL, NULL, GetModuleHandle(NULL), NULL
    );

    // 设置窗口透明、鼠标穿透
    SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);
    ShowWindow(hwnd, SW_SHOW);
    SetFocus(hwnd); // 获取键盘焦点

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

    return 0;
}
关键改动说明
  • 绘制数据持久化:用vector<DrawPixel>存储所有绘制细节,确保重绘时能完整恢复之前的绘图内容。
  • 自定义全屏窗口:窗口设置为透明、顶层,既不影响其他窗口操作,又能作为绘图载体响应重绘消息。
  • 消息驱动替代钩子:窗口直接处理鼠标和键盘事件,逻辑更简洁,避免全局钩子带来的复杂度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 08:52:33