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

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

Label
LinkLabel 

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

这段代码很好(3MB 在进程内存中保持相同的恒定速率):
 // 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 问题。

最佳答案

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

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

SelectFont 返回您需要的先前选择的字体(除非它是库存字体)来执行 DeleteFont。

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

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

关于c++ - 为什么将鼠标悬停在静态 Win32 控件上会增加内存并删除我的 GUI?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44951417/

10-11 00:56
查看更多