怎么给GDI绘制加双缓存防止闪烁

这是GDI绘制源码,怎么加上给他加上双缓存防止闪烁


#include <stdio.h>
#include<Windows.h>
#include<stdlib.h>

LRESULT __stdcall callback(HWND hwnd, UINT usermsg, WPARAM wparam, LPARAM lparam)
{
    switch (usermsg) {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(1);
        break;


    }
    return DefWindowProc(hwnd, usermsg, wparam, lparam);

}



HWND createlaiedwindow(const char wndname[255], RECT rect_layedwindow)//创建一个透明的绘制窗口
{
    WNDCLASSEX Layedwindow = { 0 };
    Layedwindow.cbSize = sizeof WNDCLASSEX;
    Layedwindow.lpfnWndProc = callback;
    Layedwindow.lpszClassName = "w;";
    Layedwindow.style = CS_HREDRAW | CS_VREDRAW;
    Layedwindow.hCursor = LoadCursor(NULL, IDC_ARROW);
    Layedwindow.hbrBackground = ((HBRUSH)RGB(0, 0, 0));
    Layedwindow.hInstance = GetModuleHandle(0);

    RegisterClassEx(&Layedwindow);//注册窗口类

    //利用窗口类创建一个实体窗口
    HWND hwnd_layedwindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, Layedwindow.lpszClassName, wndname,
        WS_POPUP, rect_layedwindow.left, rect_layedwindow.top,
        rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top,
        NULL, NULL, Layedwindow.hInstance, NULL);

    SetLayeredWindowAttributes(hwnd_layedwindow, RGB(0, 0, 0), 0, 1);//设置窗口透名

    ShowWindow(hwnd_layedwindow, SW_SHOW);

    UpdateWindow(hwnd_layedwindow);

    return hwnd_layedwindow;
}
void UpdateLayedWindow(HWND hwnd_layedwindow, HWND hwnd_game)//刷新窗口
{
    RECT rect_layedwindow{};
    GetWindowRect(hwnd_game, &rect_layedwindow);
    SetWindowPos(hwnd_layedwindow, HWND_TOPMOST,
        rect_layedwindow.left, rect_layedwindow.top,
        rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top, SWP_SHOWWINDOW);

    MoveWindow(hwnd_layedwindow, rect_layedwindow.left, rect_layedwindow.top,
        rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top, TRUE);


}

void clear(HWND hwnd_overlay, RECT rect_window)//删除上一贞数据,防满屏绘制
{
    RECT rect = { 0,0,rect_window.right - rect_window.left,rect_window.bottom - rect_window.top };
    HWND hwnd = GetForegroundWindow();
    HDC hdc = GetDC(hwnd_overlay);
    HBRUSH hbrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
    SelectObject(hdc, hbrush);
    FillRect(hdc, &rect, hbrush);
    DeleteObject(hbrush);
    ReleaseDC(hwnd_overlay, hdc);


}


namespace gdi
{
    void drawbox(HDC hdc_layedwindow, int x, int y, int boxwidth, int boxheight)
    {
        Rectangle(hdc_layedwindow, x, y, x + boxwidth, y + boxheight);


    }
    void drawline(HDC hdc_layedwindow, int x1, int y1, int x2, int y2)
    {
        MoveToEx(hdc_layedwindow, x1, y1, NULL);
        LineTo(hdc_layedwindow, x2, y2);

    }
    void drawstring(HDC hdc_layedwindow, int x, int y, COLORREF color, const char* text)
    {
        SetTextColor(hdc_layedwindow, color);
        SetBkMode(hdc_layedwindow, RGB(0, 0, 0));
        TextOutA(hdc_layedwindow, x, y, text, strlen(text));

    }
    


}


#include <iostream>
#include "GDI.h"



HWND hwnd = NULL;
HWND hwnd_layedwindow = NULL;
unsigned long processid = NULL;
RECT rect = { 0 };
HDC hdc = NULL;
HPEN hpen = NULL;
HBRUSH hbrush = NULL;

void init(const char wndname[255]);

int main()
{
    MessageBoxA(NULL, "按下确定开始绘制", "tips", MB_ICONERROR);
    init("新建文本文档.txt - 记事本");
    while (true)
    {
        
        for (size_t i = 0; i < 700; i=i+12)
        {
            
            GetWindowRect(hwnd, &rect);
            UpdateLayedWindow(hwnd_layedwindow, hwnd);
            clear(hwnd_layedwindow, rect);
            hdc = GetDC(hwnd_layedwindow);
            hpen = CreatePen(PS_INSIDEFRAME, 1, RGB(255, 0, 0));
            hbrush = (HBRUSH)GetStockObject(DEFAULT_GUI_FONT);
            SelectObject(hdc, hpen);
            SelectObject(hdc, GetStockObject(NULL_BRUSH));
            SelectObject(hdc, hbrush);//选择对象

            gdi::drawstring(hdc, 30+i, 80, RGB(255, 0, 0), "草");
            gdi::drawbox(hdc, 30+(i-5), 70, 20, 30);

            gdi::drawstring(hdc, 30 + i, 150, RGB(255, 0, 0), "草");
            gdi::drawbox(hdc, 30 + (i - 5), 140, 20, 30);
            
            gdi::drawstring(hdc, 30 , 250, RGB(255, 0, 0), "草");
            gdi::drawbox(hdc, 25 , 240, 20, 30);


            DeleteObject(hbrush);
            DeleteObject(hpen);
            ReleaseDC(hwnd_layedwindow, hdc);


        }



    }
    

}



void init(const char wndname[255])//初始化函数
{



    hwnd = FindWindow(NULL, wndname);
    if (!hwnd) {
        MessageBoxA(NULL, "未找到窗口", "tips", MB_ICONERROR);
        PostQuitMessage(0);

    }
    GetWindowThreadProcessId(hwnd, &processid);
    if (processid == NULL) {
        MessageBoxA(NULL, "取Pid失败", "tips", MB_ICONERROR);


    }
    GetWindowRect(hwnd, &rect);
    hwnd_layedwindow = createlaiedwindow(" ", rect);
    if (!hwnd_layedwindow)
    {
        MessageBoxA(NULL, "创建窗口失败", "tips", MB_ICONERROR);

    }


}

引用chatgpt部分指引作答:
要使用双缓存,需要在程序中创建两个内存DC,一个用于绘制图像,另一个用于显示。在每个绘制周期中,先绘制到内存DC中,然后再将其复制到显示DC中,从而消除闪烁。

以下是修改后的代码:

#include <stdio.h>
#include<Windows.h>
#include<stdlib.h>

LRESULT __stdcall callback(HWND hwnd, UINT usermsg, WPARAM wparam, LPARAM lparam)
{
    switch (usermsg) {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(1);
        break;


    }
    return DefWindowProc(hwnd, usermsg, wparam, lparam);

}

HWND createlaiedwindow(const char wndname[255], RECT rect_layedwindow)//创建一个透明的绘制窗口
{
    WNDCLASSEX Layedwindow = { 0 };
    Layedwindow.cbSize = sizeof WNDCLASSEX;
    Layedwindow.lpfnWndProc = callback;
    Layedwindow.lpszClassName = "w;";
    Layedwindow.style = CS_HREDRAW | CS_VREDRAW;
    Layedwindow.hCursor = LoadCursor(NULL, IDC_ARROW);
    Layedwindow.hbrBackground = ((HBRUSH)RGB(0, 0, 0));
    Layedwindow.hInstance = GetModuleHandle(0);

    RegisterClassEx(&Layedwindow);//注册窗口类

    //利用窗口类创建一个实体窗口
    HWND hwnd_layedwindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, Layedwindow.lpszClassName, wndname,
        WS_POPUP, rect_layedwindow.left, rect_layedwindow.top,
        rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top,
        NULL, NULL, Layedwindow.hInstance, NULL);

    SetLayeredWindowAttributes(hwnd_layedwindow, RGB(0, 0, 0), 0, 1);//设置窗口透名

    ShowWindow(hwnd_layedwindow, SW_SHOW);

    UpdateWindow(hwnd_layedwindow);

    return hwnd_layedwindow;
}

void UpdateLayedWindow(HWND hwnd_layedwindow, HWND hwnd_game)//刷新窗口
{
    RECT rect_layedwindow{};
    GetWindowRect(hwnd_game, &rect_layedwindow);
    SetWindowPos(hwnd_layedwindow, HWND_TOPMOST,
        rect_layedwindow.left, rect_layedwindow.top,
        rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top, SWP_SHOWWINDOW);

    MoveWindow(hwnd_layedwindow, rect_layedwindow.left, rect_layedwindow.top,
        rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top, TRUE);


}

void clear(HDC hdc_overlay, RECT rect_window)//删除上一贞数据,防满屏绘制
{
    HBRUSH hbrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
    SelectObject(hdc_overlay, hbrush);
    FillRect(hdc_overlay, &rect_window, hbrush);
    DeleteObject(hbrush);
}

namespace gdi
{
    void drawbox(HDC hdc, int x, int y, int boxwidth, int boxheight)
    {
        Rectangle(hdc, x, y, x + boxwidth,x + boxwidth, y + boxheight);
}
void drawtext(HDC hdc, const char* text, int x, int y, int width, int height, UINT format)
{
    RECT rect = { x, y, x + width, y + height };
    DrawTextA(hdc, text, -1, &rect, format);
}
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd_game = FindWindow(NULL, "MyGame");
if (!hwnd_game)
{
MessageBox(NULL, "Could not find game window!", "Error", MB_OK);
return 1;
}
HWND hwnd_overlay = createlaiedwindow("MyOverlay", RECT{ 0, 0, 800, 600 });

HDC hdc_game = GetDC(hwnd_game);
HDC hdc_overlay = CreateCompatibleDC(hdc_game);
HBITMAP hbm_overlay = CreateCompatibleBitmap(hdc_game, 800, 600);
HGDIOBJ hOld = SelectObject(hdc_overlay, hbm_overlay);

MSG msg{};
while (GetMessage(&msg, NULL, 0, 0))
{
    switch (msg.message)
    {
    case WM_QUIT:
        break;
    default:
        clear(hdc_overlay, RECT{ 0, 0, 800, 600 });

        //绘制Overlay内容
        gdi::drawbox(hdc_overlay, 100, 100, 50, 50);
        gdi::drawtext(hdc_overlay, "Hello, world!", 200, 200, 100, 20, DT_CENTER | DT_VCENTER);

        //将Overlay绘制到屏幕上
        UpdateLayedWindow(hwnd_overlay, hwnd_game);
        BitBlt(hdc_game, 0, 0, 800, 600, hdc_overlay, 0, 0, SRCCOPY);
        break;
    }

    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

//释放资源
SelectObject(hdc_overlay, hOld);
DeleteObject(hbm_overlay);
DeleteDC(hdc_overlay);
ReleaseDC(hwnd_game, hdc_game);

return 0;
}


你这是无停顿无限绘图呢? 每次绘图之间增加Sleep即可

ReleaseDC(hwnd_layedwindow, hdc); 
Sleep(33);
  • 你可以参考下这个问题的回答, 看看是否对你有帮助, 链接: https://ask.csdn.net/questions/268884
  • 我还给你找了一篇非常好的博客,你可以看看是否有帮助,链接:GDI编程
  • 除此之外, 这篇博客: GDI映射模式中的 2.需要理解的一些概念 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 以下回答由chatgpt基于相关博客总结生成:

    为了避免GDI绘制时出现画面闪烁,可以使用双缓冲技术,即先在内存中作图,然后使用BitBlt函数将做好的图复制到前台,同时禁止背景重绘,从而消除闪烁。下面是具体的代码实现步骤:

    1. 创建一个与屏幕兼容的内存DC,使用CreateCompatibleDC函数:
    HDC hMemDC = CreateCompatibleDC(hDC);
    

    其中hDC为当前窗口的DC句柄。

    1. 创建一个与屏幕兼容的位图,使用CreateCompatibleBitmap函数:
    HBITMAP hBitmap = CreateCompatibleBitmap(hDC, nWidth, nHeight);
    

    其中nWidth和nHeight分别为位图的宽度和高度。

    1. 将内存DC与位图关联,使用SelectObject函数:
    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
    
    1. 在内存DC中进行绘制操作。

    2. 使用BitBlt函数将做好的图复制到前台,同时禁止背景重绘,使用以下代码:

    RECT rect;
    GetClientRect(hWnd, &rect);
    HDC hdc = GetDC(hWnd);
    BitBlt(hdc, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, SRCCOPY);
    ReleaseDC(hWnd, hdc);
    

    其中hWnd为当前窗口的句柄。

    1. 将内存DC与位图分离,使用SelectObject函数:
    SelectObject(hMemDC, hOldBitmap);
    
    1. 删除内存DC和位图,使用DeleteDC和DeleteObject函数:
    DeleteDC(hMemDC);
    DeleteObject(hBitmap);
    

    在代码中添加上述步骤后,就可以使用双缓冲技术避免GDI绘制时出现画面闪烁了。

引用chatGPT作答,要实现双缓存防止闪烁,可以创建一个和窗口大小相同的内存缓冲区,并在内存缓冲区中进行绘制。完成绘制后,再将内存缓冲区中的内容复制到窗口绘图区域中。

以下是在原代码的基础上进行修改的部分:

1.在全局变量中添加缓冲区句柄和位图句柄:

HDC hdc_mem = NULL;
HBITMAP hbitmap_mem = NULL;

2.在 init 函数中创建缓冲区,并将句柄保存到全局变量中:

void init(const char wndname[255]) {
    // ... 窗口创建等代码 ...
    hdc_mem = CreateCompatibleDC(hdc);
    hbitmap_mem = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
    SelectObject(hdc_mem, hbitmap_mem);
}

3.在绘制函数中,先在内存缓冲区中进行绘制:

// 清空缓冲区
FillRect(hdc_mem, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
// 绘制图形
SelectObject(hdc_mem, hpen);
SelectObject(hdc_mem, hbrush);
gdi::drawbox(hdc_mem, i, i, 100, 100);
// ...

4.在绘制完成后,将缓冲区中的内容复制到窗口绘图区域中:

BitBlt(hdc_layedwindow, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hdc_mem, 0, 0, SRCCOPY);

修改后的主函数代码如下:

int main()
{
    MessageBoxA(NULL, "按下确定开始绘制", "tips", MB_ICONERROR);
    init("新建文本文档.txt - 记事本");
    while (true)
    {
        for (size_t i = 0; i < 700; i = i + 12)
        {
            GetWindowRect(hwnd, &rect);
            UpdateLayedWindow(hwnd_layedwindow, hwnd);
            // 清空缓冲区
            FillRect(hdc_mem, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
            // 绘制图形
            SelectObject(hdc_mem, hpen);
            SelectObject(hdc_mem, hbrush);
            gdi::drawbox(hdc_mem, i, i, 100, 100);
            // ...
            // 将缓冲区内容复制到窗口
            BitBlt(hdc_layedwindow, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hdc_mem, 0, 0, SRCCOPY);
        }
        Sleep(30);
    }
    return 0;
}