【问题标题】:Improve code for WM_PAINT and WM_CTLCOLORSTATIC handlers改进 WM_PAINT 和 WM_CTLCOLORSTATIC 处理程序的代码
【发布时间】:2026-02-14 12:20:06
【问题描述】:

简介及相关资料:

我已经实现了主窗口背景及其子静态控件的复杂绘制。

下图展示了它的外观。

静态控件具有SS_NOTIFY 样式,这一点很重要,因为当用户单击它们时会发生某些事情。

此时,单击它们时激活的操作不相关。

主窗口和静态控件都有渐变背景,这是通过使用GradientFill(...) API 制作的。

主窗口顶部横幅使用灰色画笔创建,网格线使用LineTo(...)MoveTo(...) API 创建。

橙色静态控件上的图,左上logo为EMF文件,右上logo为PNG文件,其他图片为位图。

橙色静态控件有 4 个子静态控件,它们分别是 owner drawnSS_NOTIFY 样式。

这是我能想到的唯一方法,这使我能够按照要求我的方式绘制控件(如果我可以改进这一点,请提出建议,我会接受任何合理的建议)。

为了绘制橙色静态控件,我决定在WM_CTLCOLORSTATIC 处理程序中绘制其背景,并在子类过程中绘制子静态控件。

从子静态控件收到的通知也在橙色静态控件子类过程中处理,因为我不知道如何将它们转发到父窗口,但由于此时它们也无关紧要而被省略。

我决定提供演示项目的链接,而不是使用代码 sn-ps 使这篇文章变得很长。

我已尝试提交尽可能小而简单的演示应用程序。

我没有吝啬评论,所以我相信所有内容都在源代码中得到了很好的涵盖和解释。

如果还有问题,请发表评论,我会尽快回复(通常是立即回复,或者至少在同一天回复)。

这里是演示项目的链接:http://www.filedropper.com/geotermistgrafika_1

重要更新:

/=============================================== =============/

方括号中的文本是问题的原始部分,但由于项目存在内存泄漏,现在被省略了。上面的链接链接到改进的版本。

[ 针对成员 xMRi 的评论进行了更新:这个链接应该没问题:http://www.filedropper.com/geotermistgrafika]

/=============================================== =============/

我在 Windows XP 上工作,使用 MS Visual Studio C++ 和纯 Win32 API。

注意一点:因为 VS 的 Express 版没有资源编辑器,所以资源文件和资源头是使用 ResEdit 从这里创建的:@ 987654323@.

问题:

当我调整窗口大小时,静态控件会轻微闪烁。

我解决问题的努力:

我相信我的代码没有内存泄漏 - 因此我怀疑这是问题所在,但由于缺乏经验,如果我的假设能够以某种方式得到证实,我将不胜感激。

我认为我已经妥善处理了WM_ERASEBKGND,并且我已经从我的窗口类中排除了样式CS_VREDRAWCS_HREDRAW - 因此不应该因此而导致闪烁。

我忘了提,我的窗口有WS_CLIPCHILDREN 样式,所以我现在提一下,以回应成员Roger Rowland 的以下评论。

我已经为两个处理程序实现了双缓冲,以避免闪烁。

问题:

  1. 如何修改演示项目中的代码以消除闪烁?

  2. 我需要有关如何优化 WM_PAINTWM_CTLCOLORSTATIC 处理程序的建议,以便我的绘画代码变得更高效、更快。

第二个问题的小注释:

我正在考虑通过在主窗口的背景上绘制整个图片来改进我的代码,并将透明的静态控件放在与该静态控件背景相对应的图片部分的顶部。

这样,我只会在我的WM_CTLCOLORSTATIC 处理程序中返回NULL_BRUSH,并在WM_PAINT 中完成所有工作。

我的想法是否正确?这行得通吗?

谢谢。 问候。

【问题讨论】:

  • 演示项目链接失效!
  • 你的主窗口有 WS_CLIPCHILDREN 样式吗?
  • @RogerRowland 是的。
  • @xMRi 我从来没有下载过任何问题,但是最近链接不时出现,所以我将项目上传到其他地方。希望问题得到解决。问候。
  • 多次绘制时会出现闪烁。你画了两次。一次绘制背景,一次绘制前景。您需要将它们合二为一。

标签: c++ winapi gdi+ gdi wm-paint


【解决方案1】:

首先,您的应用程序漏洞百出。没有寻找泄漏,但它们中的大多数应该在 WM_CTLCOLORSTATIC 中,因为您忘记删除 HBITMAP(使用这个简洁的免费软件 http://www.nirsoft.net/utils/gdi_handles.html 来查找 gdi 泄漏)。

其次,您的代码太大了。我注意到您没有使用函数,可能是因为您不知道它们的功能。例如我会使用:

void DrawBackground(HDC &hDC, SOMEINFOSTRUCT GradientInfo, LPCTSTR Text);

大大简化您的代码。

讲完了,让我们回到你的问题。在WM_CTLCOLORSTATIC 你必须返回画笔,你想用它来绘制背景。您现在正在做的是使用Bitblt() 手动绘制背景,然后返回 NULL 画笔并程序在您已经绘制的背景上绘制它。与其自己画,不如让画笔来完成。 只需使用CreatePatternBrush() 而不是最后一个Bitblt(),但是你需要照顾这个刷子,这是你应该做的:

HBRUSH TempBrush = NULL; //Create global brush

//Some Code....

case WM_CTLCOLORSTATIC:
    {
        if (TempBrush != NULL)
        {
            DeleteObject(TempBrush);
            TempBrush = NULL;
        }

        //Let's skip to the end....
        GradientFill( MemDC, vertex3, 3, &gTriangle, 1, 
        GRADIENT_FILL_TRIANGLE );

        TempBrush  = CreatePatternBrush(bmp);// these 3 line should be at the 
                                            //end of every if
        DeleteDC(MemDC);      // or put them once outside if's
        DeleteObject(bmp);  // also if you delete HDC first, you don't need to
                            //unselect hbitmap
        }
        return (LRESULT)TempBrush;
    }
    break;

case WM_CLOSE:
        {
            if (TempBrush != NULL)
            {
                DeleteObject(TempBrush);
                TempBrush = NULL;
            }
//.......

【讨论】:

  • 你说得对,我对 Winapi 相当缺乏经验。目前将东西放入函数并在适当的地方调用它们对我来说很难,但我会尝试一下。刷题问题:应该是静态的,还是只是一个全局变量?也许这是一个愚蠢的问题,但我不得不问。我会努力落实你的建议,并会尽快报告我的结果。非常感谢,我学到了很多!问候。
  • 基本上,(全局变量)==(任何函数外的静态变量)。如果您在函数内部创建变量非静态,则在函数存在后,它的值将消失。但是如果你想保持这个值直到你下次使用函数,你添加静态。
  • 我必须确定,在我得到一些结果后不久,“和你谈谈”。再次感谢,问候。
  • 看来效果是一样的。我没注意到有多大的改进。至于GDIView,你能解释一下你是如何跟踪内存泄漏的吗?我已经在 Options 菜单中打开了显示 Counters+Changes F3 的选项,所有对象的值都为零,字体除外。它们的大小在每次调整大小时都会迅速增加。我也有 4 个区域在括号中具有 +4 值。也许我在某处泄漏了字体和区域,但我不知道如何,我相信我已经正确清理了所有内容。我们将很快讨论,我不会放弃。问候。
  • 如果可以的话,上传你的固定/更新项目,我会为你添加一些整洁的东西,我也会尝试简化你的程序。除此之外,当您启动消息循环时,在“所有 gdi”(或类似的东西)列下应该有 13 个(如果您从资源加载图片则更多)对象。然后它应该增加到,比如说 24,但是当你的程序再次空闲时,它应该回到以前的数字。我注意到您的数量一直在增加,这就是我知道存在 gdi 泄漏的原因(也在任务管理器中,使用的内存正在迅速增加)。然后你必须看看其他列和......