Winapi 启用LWA_COLORKEY时如何防止颜色键明显闪烁

Winapi 启用LWA_COLORKEY时如何防止颜色键明显闪烁,winapi,transparency,layered-windows,Winapi,Transparency,Layered Windows,我有一个窗户,有时应该有一个透明的洞,有时没有。理想情况下,我们会使用SetWindowRgn,但这会禁用视觉样式,因为视觉样式不仅看起来很难看,而且不能正确绘制每监视器DPI感知,因此我尝试使用带有颜色键的分层窗口 启用颜色键时,我首先调用SetLayeredWindowAttributes(hWnd,colorkey,0,LWA\u colorkey),然后使窗口无效,以便重新绘制。此时,窗口不应包含关键点颜色。然后窗口在稍后的某个点接收到WM_PAINT,并绘制了关键颜色,但此时窗口应该设

我有一个窗户,有时应该有一个透明的洞,有时没有。理想情况下,我们会使用SetWindowRgn,但这会禁用视觉样式,因为视觉样式不仅看起来很难看,而且不能正确绘制每监视器DPI感知,因此我尝试使用带有颜色键的分层窗口

启用颜色键时,我首先调用
SetLayeredWindowAttributes(hWnd,colorkey,0,LWA\u colorkey)
,然后使窗口无效,以便重新绘制。此时,窗口不应包含关键点颜色。然后窗口在稍后的某个点接收到
WM_PAINT
,并绘制了关键颜色,但此时窗口应该设置了
LWA_COLORKEY
,因此,我希望关键颜色不可见

禁用颜色键时,我首先(同步)重新绘制窗口,使其不包含键颜色,然后禁用
WS_EX__LAYERED
,因此,我再也不希望看到键颜色

但是,当鼠标移动一个窗口时,具有以下窗口过程的窗口会在绿色、透明和背景色之间不断闪烁

似乎
SetLayeredWindowAttributes
不会立即生效(甚至在下一次
WM_PAINT
之前也不会生效)。如何确保此属性在重新绘制之前生效,或以其他方式防止关键颜色可见

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static auto const colorkey = RGB(0,255,0);
    static auto const hbrush = CreateSolidBrush(colorkey);
    static auto transparent = false;
    switch (message)
    {
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            if (transparent) {
                RECT rect{30,30,500,500};
                FillRect(hdc, &rect, hbrush);
            }
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_MOUSEMOVE:
        if (transparent) {
            transparent = false;
            RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
            SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
        } else {
            SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
            SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
            transparent = true;
            InvalidateRect(hWnd, nullptr, TRUE);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

我不认为分层窗口设计为每秒打开和关闭多次(每次切换时,windows将分配/销毁32 BPP的映像等)

SWP_FRAMECHANGED
和一个额外的擦除确实让我感觉更好,至少:

case WM_MOUSEMOVE:
    if (transparent) {
        transparent = false;
        RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INTERNALPAINT|RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN);
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
        InvalidateRect(hWnd, nullptr, true);
    } else {
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
        transparent = true;
        InvalidateRect(hWnd, nullptr, true);
    }
    TCHAR b[99];wsprintf(b,TEXT("%d tick=%d"), transparent, GetTickCount()), SetWindowText(hWnd, b);
    break;

我不认为分层窗口设计为每秒打开和关闭多次(每次切换时,windows将分配/销毁32 BPP的映像等)

SWP_FRAMECHANGED
和一个额外的擦除确实让我感觉更好,至少:

case WM_MOUSEMOVE:
    if (transparent) {
        transparent = false;
        RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INTERNALPAINT|RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN);
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
        InvalidateRect(hWnd, nullptr, true);
    } else {
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
        transparent = true;
        InvalidateRect(hWnd, nullptr, true);
    }
    TCHAR b[99];wsprintf(b,TEXT("%d tick=%d"), transparent, GetTickCount()), SetWindowText(hWnd, b);
    break;

这些分层窗口更改似乎需要一些时间才能反映在窗口的渲染中。添加睡眠可以使绿色不显示

case WM_MOUSEMOVE:
    if (transparent) {
        transparent = false;
        RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
    }
    else {
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
        Sleep(1); // Add sleep
        transparent = true;
        InvalidateRect(hWnd, nullptr, TRUE);
    }
    break;

这些分层窗口更改似乎需要一些时间才能反映在窗口的渲染中。添加睡眠可以使绿色不显示

case WM_MOUSEMOVE:
    if (transparent) {
        transparent = false;
        RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
    }
    else {
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
        Sleep(1); // Add sleep
        transparent = true;
        InvalidateRect(hWnd, nullptr, TRUE);
    }
    break;

MSDN说在改变样式后你应该做什么?@Anders:SetWindowPos(hWnd,nullptr,0,0,0,SWP_SHOWWINDOW,SWP_NOSIZE,SWP_NOMOVE,SWP_NOACTIVATE,SWP|u NOZORDER)在SetWindowLongPtr之后调用没有什么区别,如果你指的是这样的话。否则,参考您心目中的MSDN页面会很有帮助。
SWP_FRAMECHANGED
?@Anders:我喜欢MSDN说您必须做一些事情的方式,而不仅仅是出来告诉您必须做什么。但无论如何,不,这并不能解决问题。我们已经设法找到了一个使透明度保持启用状态的解决方法,但我将保留这个问题,以防其他人也有同样的问题。@Max SetLayeredWindowAttributes with color key使“以这种颜色绘制的窗口的所有像素都将是透明的”。但您似乎正在阻止以指定颜色绘制的窗口(绿色),对吗?MSDN说你在改变样式后应该做什么?@Anders:调用
SetWindowPos(hWnd,nullptr,0,0,SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP|u NOZORDER)
如果你指的是SetWindowLongPtr,那么在SetWindowLongPtr之后没有什么区别。否则,参考你心目中的MSDN页面会很有帮助。
SWP\u FRAMECHANGED
?@Anders:我喜欢MSDN说你必须做某事的方式,但不只是出来告诉你必须做什么。但无论如何,不,那是真的esn无法解决此问题。我们已设法找到一种使透明度保持启用状态的解决方法,但我将保留此问题,以防其他人有相同的问题。@Max SetLayeredWindowAttributes with color key使“窗口以此颜色绘制的所有像素都将是透明的。”但您似乎正在阻止按指定颜色(绿色)绘制的窗口,对吗?问题是该窗口可能包含用户定义的内容,因此不能保证使用单一颜色。我们通过动态选择关键颜色来解决此问题。问题是该窗口可能包含用户定义的内容,因此不能保证使用单一颜色。我们通过动态选择唱关键的颜色。