为什么将鼠标悬停在静态Win32控件上会增加内存并删除我的GUI?

时间:2017-07-06 14:13:26

标签: c++ winapi

如果不妥善处理,Windows API资源可能会导致内存泄漏。无论是否是这个问题,我都假设它是相关的。虽然我展示了我如何确定问题的来源,但我无法解决问题。

我使用Win32 API有两种类型的静态类控件,它在我的类中抽象出来:

Label
LinkLabel 

问题:每当我添加这两个控件时,当我启用setFont()或setHoverColor()行时,Visual Studio 2017的诊断工具显示进程内存(MB)从3MB增加到11MB,最终我的GUI中的所有内容空间消失了 - 就像一些着名的书店一样,从存在中消失了。

此代码很好(3MB在Process Memory中保持相同的恒定速率):

 // Linked Label
 myLinkLabel.init("http://www.google.com", 50, 450);
 myLinkLabel.setColor(0, 0, 255);
 myLinkLabel.onClick(lbl_Click);
 myLinkLabel.setFont("Arial", 40, true);
 //lbl.setHoverColor(255, 0, 0);  

 // label
 myLabel.init("A regular static label", 0, 0);
 myLabel.setColor(0, 255, 0);
 myLabel.setFont("Arial", 40);
 //myLabel.setHoverColor(255, 0, 0);

下一个代码取消注释最后一行。将鼠标悬停在myLabel上并显示红色突出显示颜色后,Process Memory的3MB增加到7MB +。它坐了一会儿,然后上升到9MB +。所以,它有些不对劲。

// Linked Label
myLinkLabel.init("http://www.google.com", 50, 450);
myLinkLabel.setColor(0, 0, 255);
myLinkLabel.onClick(lbl_Click);
myLinkLabel.setFont("Arial", 40, true);
//lbl.setHoverColor(255, 0, 0);  

// label
myLabel.init("A regular static label", 0, 0);
myLabel.setColor(0, 255, 0);
myLabel.setFont("Arial", 48);
myLabel.setHoverColor(255, 0, 0);

所以,让我们深入了解我的setHoverColor():

void Label::setHoverColor(const BYTE red, const BYTE blue, const BYTE green)
{
 m_hoverColorEnabled = true;
 m_hoverColor = RGB(red, green, blue);
}

好的,上面的代码没什么太惊人的了。这告诉我要看看WndProc。

此静态控件使用的事件是WM_SETCURSOR。

 case WM_SETCURSOR:
 { 
 HWND m_handle = (HWND)wParam; 
 // Label
 for (int i = 0; i < frm.getLabelControlCount(); i++)
 {
     if (frm.getLabelControl(i).getHandle() == m_handle)
     {
          if (frm.getLinkLabelControl(i).isLink())
          {
               // Set hover color to link  
              if (frm.getLabelControl(i).isHoverColorEnabled())  
                       frm.getLabelControl(i).setColor(frm.getLabelControl(i).getHoverColor());
                       // Update cursor to hand 
                       SetClassLongPtr(frm.getLabelControl(i).getHandle(),   GCLP_HCURSOR, (LONG_PTR)frm.getLabelControl(i).getHoverCursor());
          }
      }
      else
      {
          // Set link to blue and use default arrow  
          if (frm.getLabelControl(i).isHoverColorEnabled())
             frm.getLabelControl(i).setColor(0, 0, 255);
          SetClassLongPtr(frm.getLabelControl(i).getHandle(), GCLP_HCURSOR,
         (LONG_PTR)LoadCursor(NULL, IDC_ARROW));
      }
}

在评论此部分代码时,进程内存保持不变为3MB。取消注释此部分时,进程内存会增加。所以,这是导致问题的主要代码。

这部分代码基本上是根据当前鼠标悬停状态更新标签的文本颜色。没有盘旋时它是蓝色的,当它盘旋时它是红色的。

setColor()是以下代码:

void Label::setColor(const COLORREF color)
{
 m_foreColor = color;
 setFont(m_fontName, m_fontSize, m_bold, m_italic, m_underlined);
}

还调用setFont()来更新它:

bool Label::setFont(const std::string &fontName, const int size, const bool bold, 
 const bool italic, const bool underlined)
{  
 DWORD dwItalic;
 DWORD dwBold;
 DWORD dwUnderlined;
 SIZE linkSize;
 dwItalic = (italic) ? TRUE : FALSE;
 dwBold = (bold) ? FW_BOLD : FW_DONTCARE;
 dwUnderlined = (underlined) ? TRUE : FALSE;
 m_font = CreateFont(size, 0, 0, 0, dwBold, dwItalic, dwUnderlined, FALSE,
  ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  DEFAULT_PITCH | FF_SWISS, fontName.c_str());
 SendMessage(m_handle, WM_SETFONT, WPARAM(m_font), TRUE);
 
 // Calculate the correct width and height size  
 HDC hDC = GetDC(m_handle);
 SelectFont(hDC, m_font);
 GetTextExtentPoint32(hDC, m_text.c_str(), (int) m_text.length(), &linkSize);
 setSize(linkSize.cx, size);  
  
 // Store font information
 m_fontName = fontName;
 m_fontSize = size;
 m_bold = bold;
 m_underlined = underlined;
 m_italic = italic; 
  
 return true;
}

我的猜测是,对于创建字体并根据每个悬停重新创建字体需要进行大量更新。我的理由是除非再次设置字体,否则不会更新字体颜色。虽然我在不久的将来看到了这个空间,但我忘记删除这里的资源了吗?欢迎任何想法。很确定这也将解决LinkLabel问题。

1 个答案:

答案 0 :(得分:4)

您的基本问题是您不断生成新字体而不会发布旧字体。

每次调用setfont时,都会分配并选择一种新字体。但是当您在HDC中选择NEW字体时,您永远不会清理旧字体。

SelectFont会返回您需要的以前选择的字体(除非它是一个股票字体)来执行DeleteFont。

此外,您在GetDC调用上有更大的资源泄漏 - getDC的MS文档建议您在完成使用后使用releaseDC。

据我了解,不需要重置字体只是为了重置颜色。