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, ▭
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, ▭
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, ▭
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 窗口