C++ GDI+;闪烁

C++ GDI+;闪烁,c++,winapi,gdi+,flicker,double-buffering,C++,Winapi,Gdi+,Flicker,Double Buffering,所以我想制作一个便宜的Gyazo(截屏工具) 问题是光标坐标在闪烁,我怎样才能防止呢?我已经尝试了WM_ERASEBKGND,但是没有任何帮助 我的代码还有其他问题吗?有什么不好的做法/技巧吗 #include <Windows.h> #include <string> #include <gdiplus.h> #pragma comment (lib, "Gdiplus.lib") // Store the "screenshot" when first

所以我想制作一个便宜的Gyazo(截屏工具)

问题是光标坐标在闪烁,我怎样才能防止呢?我已经尝试了
WM_ERASEBKGND
,但是没有任何帮助

我的代码还有其他问题吗?有什么不好的做法/技巧吗

#include <Windows.h>
#include <string>
#include <gdiplus.h>
#pragma comment (lib, "Gdiplus.lib")

// Store the "screenshot" when first launching the program
HBITMAP hbm;

// This draws the cursor coordinates close to the cursor
void DrawCursorCoords(Gdiplus::Graphics &graphics, Gdiplus::Bitmap &bitmap, Gdiplus::Color c)
{
    POINT cursorPos;
    GetCursorPos(&cursorPos);

    std::wstring x = std::to_wstring(cursorPos.x);
    std::wstring y = std::to_wstring(cursorPos.y);

    graphics.DrawString(x.c_str(), x.length(), &Gdiplus::Font(L"Consolas", 16), Gdiplus::PointF(cursorPos.x, cursorPos.y), &Gdiplus::SolidBrush(c));
    graphics.DrawString(y.c_str(), y.length(), &Gdiplus::Font(L"Consolas", 16), Gdiplus::PointF(cursorPos.x, cursorPos.y + 16), &Gdiplus::SolidBrush(c));
}

// Paint our stuff
void Paint(HDC &hdc)
{
    Gdiplus::Graphics * gfx = new Gdiplus::Graphics(hdc);
    Gdiplus::Bitmap * bmap = new Gdiplus::Bitmap(hbm, (HPALETTE)0);

    gfx->DrawImage(bmap, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));

    if (GetAsyncKeyState(VK_LBUTTON))
        DrawCursorCoords(*gfx, *bmap, Gdiplus::Color::Red);
    else
        DrawCursorCoords(*gfx, *bmap, Gdiplus::Color::Green);

    delete gfx;
    delete bmap;
}

LRESULT APIENTRY WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_PAINT:
    {
        hdc = BeginPaint(hwnd, &ps);

        Paint(hdc);

        EndPaint(hwnd, &ps);
        break;
    }

    case WM_TIMER:
    {
        InvalidateRect(hwnd, NULL, NULL);
        break;
    }

    case WM_CLOSE:
    {
        DestroyWindow(hwnd);
        break;
    }

    case WM_RBUTTONUP:
    case WM_KEYDOWN:
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        break;
    }

    case WM_ERASEBKGND: return TRUE;

    default: return DefWindowProc(hwnd, message, wParam, lParam);

    }

    return 0L;
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    char className[] = "_className";
    HWND hwnd = NULL;

    WNDCLASS wc;
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_CROSS);
    wc.hbrBackground = (HBRUSH)(0);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = className;

    if (RegisterClass(&wc))
    {
        hwnd = CreateWindowEx(
            WS_EX_TRANSPARENT | WS_EX_TOPMOST,
            className, NULL,
            WS_POPUP | WS_VISIBLE,
            0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
            NULL, NULL, hInstance, NULL);
    }

    if (!hwnd) return FALSE;

    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);

    SetTimer(hwnd, 1, 1, NULL);

    return TRUE;
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    // Take a screenshot and store it to 'hbm'
    HWND hwndDesktop = GetDesktopWindow();
    HDC hdcDesktop = GetDC(hwndDesktop);
    HDC hdcCapture = CreateCompatibleDC(hdcDesktop);
    hbm = CreateCompatibleBitmap(hdcDesktop, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
    SelectObject(hdcCapture, hbm);
    BitBlt(hdcCapture, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
        hdcDesktop, 0, 0, SRCCOPY | CAPTUREBLT);

    // Start GDI+
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    MSG msg;

    InitInstance(hInstance, nCmdShow);

    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    Gdiplus::GdiplusShutdown(gdiplusToken);
    DeleteObject(hbm);
    ReleaseDC(hwndDesktop, hdcDesktop);
    DeleteDC(hdcDesktop);
    DeleteDC(hdcCapture);

    return msg.wParam;
}
#包括
#包括
#包括
#pragma注释(lib,“Gdiplus.lib”)
//首次启动程序时存储“屏幕截图”
HBITMAP-hbm;
//这将在靠近光标的位置绘制光标坐标
void DrawCursorCoords(Gdiplus::Graphics&Graphics,Gdiplus::Bitmap&Bitmap,Gdiplus::Color c)
{
点游标;
GetCursorPos(&cursorPos);
std::wstring x=std::to_wstring(cursorPos.x);
std::wstring y=std::towstring(cursorPos.y);
graphics.DrawString(x.c_str(),x.length(),&Gdiplus::Font(L“Consolas”,16),Gdiplus::PointF(cursorPos.x,cursorPos.y),&Gdiplus::SolidBrush(c));
graphics.DrawString(y.c_str(),y.length(),&Gdiplus::Font(L“Consolas”,16),Gdiplus::PointF(cursorPos.x,cursorPos.y+16),&Gdiplus::SolidBrush(c));
}
//油漆我们的东西
空隙涂料(HDC和HDC)
{
Gdiplus::Graphics*gfx=新的Gdiplus::Graphics(hdc);
Gdiplus::Bitmap*bmap=新的Gdiplus::Bitmap(hbm,(HPALETTE)0);
gfx->DrawImage(bmap,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CXSCREEN));
if(GetAsyncKeyState(VK_LBUTTON))
DrawCursorCoords(*gfx,*bmap,Gdiplus::颜色::红色);
其他的
DrawCursorCoords(*gfx,*bmap,Gdiplus::Color::Green);
删除gfx;
删除bmap;
}
LRESULT APIENTRY WndProc(HWND HWND,UINT消息,WPARAM WPARAM,LPARAM LPARAM)
{
PAINTSTRUCT-ps;
HDC-HDC;
开关(信息)
{
案例WM_油漆:
{
hdc=开始喷漆(hwnd和ps);
涂料(hdc);
端漆(hwnd和ps);
打破
}
案例WM_计时器:
{
无效(hwnd,NULL,NULL);
打破
}
案例WM_结束:
{
窗口(hwnd);
打破
}
案例WM_RBUTTONUP:
案例WM_键控:
案例WM_销毁:
{
PostQuitMessage(0);
打破
}
案例WM_ERASEBKGND:返回TRUE;
默认值:返回DefWindowProc(hwnd、message、wParam、lParam);
}
返回0升;
}
BOOL InitInstance(HINSTANCE HINSTANCE,int nCmdShow)
{
char className[]=“_className”;
HWND HWND=NULL;
WNDCLASS wc;
wc.style=0;
wc.lpfnWndProc=WndProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=hInstance;
wc.hIcon=NULL;
wc.hCursor=LoadCursor(空,IDC_交叉);
wc.hbrBackground=(HBRUSH)(0);
wc.lpszMenuName=NULL;
wc.lpszClassName=className;
if(注册表类(&wc))
{
hwnd=CreateWindowEx(
WS_EX_透明| WS_EX_顶部,
类名,NULL,
WS|U弹出窗口| WS|U可见,
0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),
NULL,NULL,hInstance,NULL);
}
如果(!hwnd)返回FALSE;
展示窗口(hwnd、SW_展示);
更新窗口(hwnd);
设置计时器(hwnd,1,1,NULL);
返回TRUE;
}
int APICENTRY WinMain(HINSTANCE HINSTANCE、HINSTANCE hPrevInstance、LPSTR lpCmdLine、int nCmdShow)
{
//拍摄屏幕截图并将其存储到“hbm”
HWND hwndDesktop=GetDesktopWindow();
HDC hdcDesktop=GetDC(hwndDesktop);
HDC hdcCapture=CreateCompatibleDC(hdcDesktop);
hbm=CreateCompatibleBitmap(hdcDesktop、GetSystemMetrics(SM_CXSCREEN)、GetSystemMetrics(SM_CYSCREEN));
选择对象(hdcCapture,hbm);
BitBlt(hdcCapture,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),
hdcDesktop,0,0,SRCCOPY | CAPTUREBLT);
//启动GDI+
Gdiplus::GdiplusStartupInput GdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken,&gdiplusStartupInput,NULL);
味精;
InitInstance(hInstance、nCmdShow);
while(GetMessage(&msg,NULL,0,0)>0)
{
翻译信息(&msg);
发送消息(&msg);
}
Gdiplus::GdiplusShutdown(gdiplusToken);
删除对象(hbm);
ReleaseDC(hwndDesktop、hdcDesktop);
DeleteDC(hdcDesktop);
DeleteDC(hdcCapture);
返回msg.wParam;
}

您需要使用缓冲区进行绘制。您可以创建内存dc,或使用
BeginBufferedPaint

#include <uxtheme.h>
#pragma comment (lib, "uxtheme.lib")
...

case WM_PAINT:
{
    hdc = BeginPaint(hwnd, &ps);
    RECT rc;
    GetClientRect(hwnd, &rc);
    HDC memdc;
    auto hbuff = BeginBufferedPaint(hdc, &rc, BPBF_COMPATIBLEBITMAP, NULL, &memdc);
    Paint(memdc);
    EndBufferedPaint(hbuff, TRUE);
    EndPaint(hwnd, &ps);
    break;
}
您可能还想使用
SetLayeredWindowAttributes
浏览
WS_EX_LAYERED
标志,这将创建一个透明窗口,显示其下方的桌面。对于简单的文本绘制,它实际上不需要GDI+

此外,Gdiplus的大多数类都有不同的构造函数,这样可以避免使用
new
/
delete
。例如:

void DrawCursorCoords(Gdiplus::Graphics &graphics, Gdiplus::Bitmap&, Gdiplus::Color c)
{
    POINT cursorPos;
    GetCursorPos(&cursorPos);

    std::wstring x = std::to_wstring(cursorPos.x);
    std::wstring y = std::to_wstring(cursorPos.y);

    Gdiplus::Font font(L"Consolas", (Gdiplus::REAL)16);
    Gdiplus::SolidBrush brush(c);

    graphics.DrawString(x.c_str(), (int)x.length(), &font,
        Gdiplus::PointF((Gdiplus::REAL)cursorPos.x, (Gdiplus::REAL)cursorPos.y),
        &brush);

    graphics.DrawString(y.c_str(), (int)y.length(), &font,
        Gdiplus::PointF((Gdiplus::REAL)cursorPos.x, (Gdiplus::REAL)(cursorPos.y + 16)),
        &brush);
}

void Paint(HDC &hdc)
{
    Gdiplus::Graphics gfx(hdc);
    Gdiplus::Bitmap bmap(hbm, (HPALETTE)0);

    gfx.DrawImage(&bmap, 0, 0,
        GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));

    if(GetAsyncKeyState(VK_LBUTTON))
        DrawCursorCoords(gfx, bmap, (Gdiplus::Color)Gdiplus::Color::Red);
    else
        DrawCursorCoords(gfx, bmap, (Gdiplus::Color)Gdiplus::Color::Green);
}
或者您可以声明
Gdiplus::Bitmap*bmap=new-Gdiplus::Bitmap(hbm,NULL)
这将创建一个
hbm
的副本,因此您可以通过将
bmap
声明为全局,并仅创建/销毁一次,从而使此操作更加高效

还应按如下方式还原旧位图:

 hbm = CreateCompatibleBitmap(...)
 auto oldbitmap = SelectObject(hdcCapture, hbm);
 ...
 //cleanup
 SelectObject(hdcCapture, oldbitmap);
 DeleteObject(hbm);

尽管如果不还原旧位图,windows仍将尝试修复错误,因此大多数情况下不会出现任何问题。

导致闪烁的是DrawImage()调用。它覆盖先前绘制的文本,然后DrawCursorCoords()将其放回原处。需要双缓冲来消除它,谷歌“c++gdi+双缓冲”才能获得好的点击率。@JesperJuhl:所以。。。您建议使用哪种C++设备来实现一个平台无关的GUI?如果你不能说出其中一个,那么是什么让你选择正确的工具成为“坏习惯”@IInspectable我的第一选择是Qt、SFML和SDL。很可能是Qt。希望这算是“命名一个”。@JesperJuhl:因此,与其为只针对Windows的工具编写特定于Windows的代码,不如建议添加另一个依赖项,现在将其设置为特定于Windows和特定于库?最近有没有将过度工程推广到最佳实践中?此外,Qt是一个玩具,它永远不会是专业的
ReleaseDC(hwndDesktop, hdcDesktop);
DeleteDC(hdcDesktop); //<- not required
hbm = CreateCompatibleBitmap(...)
SelectObject(hdcCapture, hbm);
...
DeleteObject(hbm);
 hbm = CreateCompatibleBitmap(...)
 auto oldbitmap = SelectObject(hdcCapture, hbm);
 ...
 //cleanup
 SelectObject(hdcCapture, oldbitmap);
 DeleteObject(hbm);