Visual-C Win32 API 分层窗口闪烁

Posted

技术标签:

【中文标题】Visual-C Win32 API 分层窗口闪烁【英文标题】:Visual-C Win32 API Layered Window Flickering 【发布时间】:2016-09-24 01:20:02 【问题描述】:

我一直在摆弄使用 Windows API 的自定义窗口。因此,我一直在使用扩展分层 Windows。我已经绘制了窗口,它可以工作。但是,在调整窗口大小时,调用 redraw/UpdateLayeredWindow(和 UpdateLayeredWindowIndirect)方法时会出现奇怪的闪烁/故障 [(Screenshot) ][1]。我在这里做错了什么吗?

void redrawBaseWindow(HWND window) 
    RECT rect;
    GetWindowRect(window, &rect);
    int elipseDiam = ((rect.right - rect.left)+(rect.bottom-rect.top))/2*0.025;
    HDC mainDC = GetDC(NULL);
    POINT destination =  rect.left,rect.top ;
    POINT zero = destination;
    SIZE size =  rect.right - rect.left,rect.bottom - rect.top ;
    BLENDFUNCTION bf;
    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.AlphaFormat = 0;
    bf.SourceConstantAlpha = 255;
    HBRUSH brush = CreateSolidBrush(RGB(255, 0, 100));
    HBRUSH oldbrush = SelectObject(mainDC, brush);
    HPEN pen = CreatePen(PS_NULL, 0, RGB(0, 0, 0));
    HPEN oldPen = SelectObject(mainDC, pen);
    HBITMAP bmp = CreateCompatibleBitmap(mainDC, size.cx,size.cy);
    HBITMAP oldBmp = SelectObject(mainDC, bmp);
    FillRect(mainDC, &rect, CreateSolidBrush(RGB(0, 0, 0)));
    Rectangle(mainDC, rect.left, rect.top + elipseDiam/2, rect.right, rect.bottom - elipseDiam/2);
    Rectangle(mainDC, rect.left + elipseDiam/2, rect.top, rect.right - elipseDiam/2, rect.bottom);
    UPDATELAYEREDWINDOWINFO updated =  sizeof(UPDATELAYEREDWINDOWINFO), NULL, &destination, &size, mainDC, &zero, RGB(0,0,0),&bf, ULW_COLORKEY, ▭
    UpdateLayeredWindowIndirect(window, &updated);
    //BOOL error = UpdateLayeredWindow(window, NULL, &destination, &size, mainDC, &zero, RGB(0, 0, 0), &bf, ULW_COLORKEY);
    SelectObject(mainDC, oldbrush);
    SelectObject(mainDC, oldBmp);
    SelectObject(mainDC, oldPen);
    DeleteObject(brush);
    DeleteObject(bmp);
    DeleteObject(pen);

以防万一,Window Message Proc.

long __stdcall WndProc(HWND window, unsigned int msg, WPARAM wp, LPARAM lp) 
    PAINTSTRUCT ps;
    HDC hdc;
    TCHAR greeting[] = "Hello, World!";
    RECT rect_window;
    GetWindowRect(window, &rect_window);
    switch (msg) 
    case WM_SYSKEYDOWN:
        MessageBeep(MB_ICONERROR);
    case WM_CREATE:
    break;
case WM_DESTROY:
    PostQuitMessage(0);
    break;
case WM_NCHITTEST:;
    int x = LOWORD(lp);
    int y = HIWORD(lp);
    if (x > rect_window.left && x<rect_window.right && y>rect_window.top && y < rect_window.top + 25)
        return HTCAPTION;
    if (x>=rect_window.left-3 && x<=rect_window.left+3) 
        return HTLEFT;
    if (x >= rect_window.right - 3 && x <= rect_window.right + 3)
        return HTRIGHT;
    if (y >= rect_window.top - 3 && y <= rect_window.top + 3)
        return HTTOP;
    if (y >= rect_window.bottom - 3 && y <= rect_window.bottom + 3)
        return HTBOTTOM;
    break;
case WM_LBUTTONDOWN:
    break;
case WM_SIZE:
    redrawBaseWindow(window);
    break;
case WM_PAINT:
    break;

return DefWindowProc(window, msg, wp, lp);

编辑: Gif of the glitch

编辑 2: 在 UpdateLayeredWindow 中使用 CreateCompatibleDC 值 [也不起作用]

void redrawBaseWindow(HWND window) 
    RECT rect;
    GetWindowRect(window, &rect);
    int elipseDiam = ((rect.right - rect.left)+(rect.bottom-rect.top))/2*0.025;
    HDC mainDC = GetDC(NULL);
    HDC memDC = CreateCompatibleDC(mainDC);
    POINT destination =  rect.left,rect.top ;
    POINT zero = destination;
    SIZE size =  rect.right - rect.left,rect.bottom - rect.top ;
    BLENDFUNCTION bf;
    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.AlphaFormat = 0;
    bf.SourceConstantAlpha = 255;
    HBRUSH brush = CreateSolidBrush(RGB(255, 0, 100));
    HBRUSH oldbrush = SelectObject(memDC, brush);
    HPEN pen = CreatePen(PS_NULL, 0, RGB(0, 0, 0));
    HPEN oldPen = SelectObject(memDC, pen);
    HBITMAP bmp = CreateCompatibleBitmap(memDC, size.cx,size.cy);
    HBITMAP oldBmp = SelectObject(memDC, bmp);
    FillRect(memDC, &rect, CreateSolidBrush(RGB(0, 0, 0)));
    Rectangle(memDC, rect.left, rect.top + elipseDiam/2, rect.right, rect.bottom - elipseDiam/2);
    Rectangle(memDC, rect.left + elipseDiam/2, rect.top, rect.right - elipseDiam/2, rect.bottom);
    Ellipse(memDC, rect.left + elipseDiam, rect.top + elipseDiam, rect.left, rect.top); //Top-Left
    Ellipse(memDC, rect.right - elipseDiam, rect.top, rect.right, rect.top + elipseDiam); //Top-Right
    Ellipse(memDC, rect.left, rect.bottom, rect.left + elipseDiam, rect.bottom - elipseDiam); //Bottom-Left
    Ellipse(memDC, rect.right - elipseDiam, rect.bottom, rect.right, rect.bottom - elipseDiam); //Bottom-Right
    UPDATELAYEREDWINDOWINFO updated =  sizeof(UPDATELAYEREDWINDOWINFO), NULL, &destination, &size, memDC, &zero, RGB(0,0,0),&bf, ULW_COLORKEY, &rect;
    UpdateLayeredWindowIndirect(window, &updated);
    //BOOL error = UpdateLayeredWindow(window, NULL, &destination, &size, mainDC, &zero, RGB(0, 0, 0), &bf, ULW_COLORKEY);
    SelectObject(memDC, oldbrush);
    SelectObject(memDC, oldBmp);
    SelectObject(memDC, oldPen);
    DeleteObject(brush);
    DeleteObject(bmp);
    DeleteObject(pen);
    ReleaseDC(NULL,mainDC);
    ReleaseDC(NULL,memDC);

编辑 3:在 CreateCompatibleDC 设备上删除 DC,以及在屏幕 DC 而不是 CompatibleDC 上创建位图

void redrawBaseWindow(HWND window) 
    RECT rect;
    GetWindowRect(window, &rect);
    int elipseDiam = ((rect.right - rect.left)+(rect.bottom-rect.top))/2*0.025;
    HDC mainDC = GetDC(NULL);
    HDC memDC = CreateCompatibleDC(mainDC);
    POINT destination =  rect.left,rect.top ;
    POINT zero = destination;
    SIZE size =  rect.right - rect.left,rect.bottom - rect.top ;
    BLENDFUNCTION bf;
    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.AlphaFormat = 0;
    bf.SourceConstantAlpha = 255;
    HBRUSH brush = CreateSolidBrush(RGB(255, 0, 100));
    HBRUSH oldbrush = SelectObject(memDC, brush);
    HPEN pen = CreatePen(PS_NULL, 0, RGB(0, 0, 0));
    HPEN oldPen = SelectObject(memDC, pen);
    HBITMAP bmp = CreateCompatibleBitmap(mainDC, size.cx,size.cy);
    HBITMAP oldBmp = SelectObject(memDC, bmp);
    FillRect(memDC, &rect, CreateSolidBrush(RGB(0, 0, 0)));
    Rectangle(memDC, rect.left, rect.top + elipseDiam/2, rect.right, rect.bottom - elipseDiam/2);
    Rectangle(memDC, rect.left + elipseDiam/2, rect.top, rect.right - elipseDiam/2, rect.bottom);
    Ellipse(memDC, rect.left + elipseDiam, rect.top + elipseDiam, rect.left, rect.top); //Top-Left
    Ellipse(memDC, rect.right - elipseDiam, rect.top, rect.right, rect.top + elipseDiam); //Top-Right
    Ellipse(memDC, rect.left, rect.bottom, rect.left + elipseDiam, rect.bottom - elipseDiam); //Bottom-Left
    Ellipse(memDC, rect.right - elipseDiam, rect.bottom, rect.right, rect.bottom - elipseDiam); //Bottom-Right
    UPDATELAYEREDWINDOWINFO updated =  sizeof(UPDATELAYEREDWINDOWINFO), NULL, &destination, &size, memDC, &zero, RGB(0,0,0),&bf, ULW_COLORKEY, &rect;
    UpdateLayeredWindowIndirect(window, &updated);
    //BOOL error = UpdateLayeredWindow(window, NULL, &destination, &size, mainDC, &zero, RGB(0, 0, 0), &bf, ULW_COLORKEY);
    SelectObject(memDC, oldbrush);
    SelectObject(memDC, oldBmp);
    SelectObject(memDC, oldPen);
    DeleteObject(brush);
    DeleteObject(bmp);
    DeleteObject(pen);
    DeleteDC(memDC);
    ReleaseDC(NULL,mainDC);

原始闪烁已修复。谢谢 [乔纳森·波塔] 然而发生了另​​一个错误:This

【问题讨论】:

请创建一个动画 gif 截图,我在您的图像上看不到问题。 会的,等会儿 在从GetDC 获得的 DC 中选择不同的位图有点奇怪,我不确定是否支持将屏幕的 DC 传递给 UpdateLayeredWindow。正常用法是将您的位图选择到从CreateCompatibleDC 获得的内存 DC 中,渲染到其中并从中更新窗口。您还有 GDI 资源泄漏(未调用 ReleaseDC)。 在创建内存 DC 时,其中有一个单色位图,因此如果您在其上调用 CreateCompatibleBitmap,您最终也会得到一个单色位图。您需要与屏幕 DC 兼容的位图。此外,从CreateCompatibleDC 获得的 DC 使用 DeleteDC 释放,而不是 ReleaseDC 请不要将答案编辑到您的问题中。而是发布答案(请参阅Can I answer my own question?)。如果您有新问题,请点击 按钮。 【参考方案1】:
void redrawBaseWindow(HWND window) 
RECT rect;
GetWindowRect(window, &rect);
int elipseDiam = ((rect.right - rect.left)+(rect.bottom-rect.top))/2*0.025;
HDC mainDC = GetDC(NULL);
HDC memDC = CreateCompatibleDC(mainDC);
POINT destination =  rect.left,rect.top ;
POINT zero = destination;
SIZE size =  rect.right - rect.left,rect.bottom - rect.top ;
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = 0;
bf.SourceConstantAlpha = 255;
HBRUSH brush = CreateSolidBrush(RGB(255, 0, 100));
HBRUSH oldbrush = SelectObject(memDC, brush);
HPEN pen = CreatePen(PS_NULL, 0, RGB(0, 0, 0));
HPEN oldPen = SelectObject(memDC, pen);
HBITMAP bmp = CreateCompatibleBitmap(mainDC, size.cx,size.cy);
HBITMAP oldBmp = SelectObject(memDC, bmp);
FillRect(memDC, &rect, CreateSolidBrush(RGB(0, 0, 0)));
Rectangle(memDC, rect.left, rect.top + elipseDiam/2, rect.right, rect.bottom - elipseDiam/2);
Rectangle(memDC, rect.left + elipseDiam/2, rect.top, rect.right - elipseDiam/2, rect.bottom);
Ellipse(memDC, rect.left + elipseDiam, rect.top + elipseDiam, rect.left, rect.top); //Top-Left
Ellipse(memDC, rect.right - elipseDiam, rect.top, rect.right, rect.top + elipseDiam); //Top-Right
Ellipse(memDC, rect.left, rect.bottom, rect.left + elipseDiam, rect.bottom - elipseDiam); //Bottom-Left
Ellipse(memDC, rect.right - elipseDiam, rect.bottom, rect.right, rect.bottom - elipseDiam); //Bottom-Right
UPDATELAYEREDWINDOWINFO updated =  sizeof(UPDATELAYEREDWINDOWINFO), NULL, &destination, &size, memDC, &zero, RGB(0,0,0),&bf, ULW_COLORKEY, &rect;
UpdateLayeredWindowIndirect(window, &updated);
//BOOL error = UpdateLayeredWindow(window, NULL, &destination, &size, mainDC, &zero, RGB(0, 0, 0), &bf, ULW_COLORKEY);
SelectObject(memDC, oldbrush);
SelectObject(memDC, oldBmp);
SelectObject(memDC, oldPen);
DeleteObject(brush);
DeleteObject(bmp);
DeleteObject(pen);
DeleteDC(memDC);
ReleaseDC(NULL,mainDC);

谢天谢地,这解决了我的问题。

【讨论】:

以上是关于Visual-C Win32 API 分层窗口闪烁的主要内容,如果未能解决你的问题,请参考以下文章

即使使用 Win32GUI,cx_Freeze 也会闪烁 cmd 窗口

win32-gdi系统驱动的WM_PAINT无闪烁吗?

专题:DUILIB Win32 透明效果

Win32 API 打开新窗口

python win32api win32gui win32con 窗口句柄 发送消息 常用方法 键盘输入

Win32 API 工具窗口