【问题标题】:Rich Edit Control paints whole application black after unminimizeRich Edit Control 在取消最小化后将整个应用程序涂成黑色
【发布时间】:2020-02-25 04:56:14
【问题描述】:

已解决:我已发布我的解决方案作为答案。

这是我的问题:(gif)(如果我在任何 WM_PAINT 消息之前取消最小化时重新加载位图以绘制背景图像,就可以解决。)

每当我取消最小化应用程序、应用程序首次显示 OK 的时间(除非您正在使用调试器单步执行,否则会短暂显示一秒钟),然后突然变黑(或在应用程序窗口中设置为 hbrBackground 的任何颜色)时,就会发生这种情况类)。我可以通过重新加载 WM_PAINT 中使用的 HBITMAP 来防止这种行为,这些 HBITMAP 是全局变量,并在应用启动时使用其相应的值进行初始化。

gif 开始显示应用程序在最小化后重新打开,调试器逐步通过 Rich Edit Control 消息循环的父窗口,所有窗口的背景变为黑色之前和之后的时刻,然后步入 Rich编辑 Control 子类消息循环,进入 WM_PAINT。

  • 如果我在应用程序之间切换而相关应用程序之前从未被最小化过,则不会发生这种情况。
  • 如果富编辑控件 (RICHEDIT50W) 之前没有显示任何文本,即不会发生这种情况。如果没有显示任何文字,则应用可以正常工作。
  • 这是窗口树:
    • 主窗口
      • 一些子窗口
      • 子窗口 1
        • 丰富的编辑控件

步出子窗口1 WndProc;进入 WndSubclassProcWhatever 回调中 Rich Edit Control 的 WM_PAINT。

在意识到在 unminimize 之后调用 LoadImage() 可以解决后台问题之前,我做了一些事情:

  • 使用子类拦截 Rich Edit Control 的消息循环,并处理(以及在每个其他窗口中)消息:WM_COMMAND、WM_IME_NOTIFY、WM_NCPAINT、WM_WINDOWPOSCHANGED、WM_WINDOWPOSCHANGING、WM_ERASEBKGND... 主要返回与DefSubclassProc/DefWindowProcW.

  • 应用重新打开后立即调用 ValidateRect()...

    在此之前发生过这种情况,而不是整个应用变黑,只有文本“突出显示”或 Rich Edit Control 父级变黑,整个应用在另一个最小化取消最小化周期后变黑。

我在更新的 Windows 10 中使用具有默认设置的 Visual Studio Community 2019,并且在发布和调试版本中都看到了这个问题。

我现在期待防止位图“卸载”,从而节省许多看似不必要的 LoadImage() 调用。 已解决

我尝试上传代码的最小版本,但行为结果并不完全相同,所以感谢之前给出的答案!

【问题讨论】:

  • 请发布一个可以构建和运行的代码的最小版本,它会显示错误。否则很难猜出哪里出了问题。
  • @jwezorek 完成!抱歉,我希望有人可以毫不费力地解决我的问题。请注意,错误必须具有将窗口变成无边界无标题的设置,但我想保持相同的方式!

标签: winapi richedit wm-paint


【解决方案1】:

这与 Rich Edit Control 无关,即使你删除了所有的控件,也会出现这种情况。

您只需在注册窗口时为窗口背景添加默认颜色即可。

这里:

ATOM MyRegisterClass(HINSTANCE hInstance)
    {
        WNDCLASSEXW wcex;

        wcex.cbSize = sizeof(WNDCLASSEX);

        wcex.style = CS_DBLCLKS;
        wcex.lpfnWndProc = WndProc;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = 0;
        wcex.hInstance = hInstance;
        wcex.hIcon = NULL; // Procesás WM_GETICON
        wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
        wcex.hbrBackground = NULL;
        wcex.lpszMenuName = NULL;
        wcex.lpszClassName = L"mainWindowClass";
        wcex.hIconSm = NULL; // Procesás WM_GETICON

        return RegisterClassExW(&wcex);
    }

在最小化窗口后再次点击会导致它使用默认背景颜色重绘,但是你在这里将背景颜色设置为NULL。所以试着把wcex.hbrBackground = NULL改成wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1)

更新:

听起来你和我之前遇到的问题一样。

这是我之前的代码:

 case WM_PAINT:
        {
            PAINTSTRUCT ps;
           HDC hdc =  BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here... 
           hdcMem = CreateCompatibleDC(hdc);
           HGDIOBJ previousbit = SelectObject(hdcMem, hBmp);
           AlphaBlend(hdc, 0, 0, width_1, height_1, hdcMem, 0, 0, width_1, height_1, bf);
           DeleteDC(hdcMem);
           EndPaint(hWnd, &ps);
        }
        break;
case WM_MOUSEWHEEL:
    {
        if (GET_WHEEL_DELTA_WPARAM(wParam) > 0 && bTrans <= 234)
        {
            bTrans += 20;
            bf.SourceConstantAlpha = bTrans;
            InvalidateRect(hWnd, NULL, TRUE);
        }
        if (GET_WHEEL_DELTA_WPARAM(wParam) < 0 && bTrans >= 20)
        {
            bTrans -= 20;
            bf.SourceConstantAlpha = bTrans;
            InvalidateRect(hWnd, NULL, TRUE);
        }
        return 0;
    }

我滑动鼠标滚轮,它会触发InvalidateRect(hWnd, NULL, TRUE);

但是如果我删除DeleteDC(hdcMem),它会返回一个没有图片的主窗口。

调试快照是:

是的,你可以找到previousbit == NULL

正如@Remy Lebeau 所说,您正在泄漏SelectObject() 返回的HBITMAP,并授予HDC 可能在背后破坏您的bitmapBackgroundMainWindow 的权限。

这是主要原因。

【讨论】:

  • 非常感谢您的回答。我真的很抱歉,你是对的,我的最小示例没有显示 gif 上存在的行为,并且更改它的背景是一个修复。我已经测试过更改我的应用程序主窗口背景,但问题仍然存在。我求助于上传整个应用程序(目前还不是很广泛),希望不要泄露个人信息!再次感谢您的帮助,并希望您没有在前面的示例中摆弄太久,对不起。
  • 我在这个堆栈中还没有 15 名声望,但你的回答适合我的问题,所以只要我能够投票,你就会得到支持!
  • 再次感谢您的帮助!我已经发布了该问题的解决方案。如果您碰巧弄清楚如何不正确删除 HDC 可能会转化为 WM_PAINT 中使用的全局 HBITMAP 在取消最小化时被删除,请不要犹豫扩展该主题或发布比我更好的答案,如果是,我会接受您的答案改进了我的!
  • @JuanManuelLópezManzano 不要太在意,我很乐意随时为您提供帮助。
【解决方案2】:

调用 DeleteObject() 解决了这个问题。

这是位图背景窗口 WM_PAINT 消息之一的代码,通过相应的 DeleteObject() 调用修复:

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    HDC temporaryDC = CreateCompatibleDC(hdc);
    BITMAP bitmapToBitBlt;
    HGDIOBJ hgdiobjToBitBlt = SelectObject(temporaryDC, bitmapBackgroundMainWindow);
    GetObjectW(bitmapBackgroundMainWindow, sizeof(BITMAP), &bitmapToBitBlt);
    BitBlt(hdc, 0, 0, bitmapToBitBlt.bmWidth, bitmapToBitBlt.bmHeight, temporaryDC, 0, 0, SRCCOPY);
    DeleteObject(temporaryDC); // This fixes the app.
    EndPaint(hWnd, &ps);
    return 0;

作为Windows Docs状态,调用CreateCompatibleDC()后:

当你不再需要内存 DC 时,调用 DeleteDC 函数。我们建议您调用 DeleteDC 来删除 DC。但是,您也可以使用 HDC 调用 DeleteObject 来删除 DC。

这如何转化为我的应用程序的意外行为,我不知道,请随时在 cmets 中澄清!

【讨论】:

  • 您正在泄露SelectObject() 返回的HBITMAP,并授予HDC 可能在背后破坏您的bitmapBackgroundMainWindow 的权限。在销毁HDC 之前,您需要将SelectObject() 原来的HBITMAP 重新变为HDC:“[SelectObject] 返回先前选择的指定类型的对象。应用程序应始终替换使用新对象完成绘制后使用原始默认对象的新对象“此外,使用DeleteDC() 来销毁HDC 而不是DeleteObject(),如文档所述。
猜你喜欢
  • 2011-01-17
  • 2016-03-11
  • 1970-01-01
  • 1970-01-01
  • 2011-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多