MFC自绘按钮内存泄漏

梁阿狸 2015-04-15 09:42:57
把对应的按钮替换成系统默认的CButton按钮,程序正常,但是用自绘的CIconButton则会内存泄漏。请帮忙看一下问题出在哪里,谢谢!
class AFX_EXT_CLASS CIconButton : public CButton
{
private:
// CDC m_NormalDC;
// CDC m_DownDC;
// CDC m_DisableDC;
//
// CBitmap m_NormalBmp;
// CBitmap m_DownBmp;
// CBitmap m_DisableBmp;
HDC m_NormalDC;
HDC m_DownDC;
HDC m_DisableDC;

HBITMAP m_NormalBmp;
HBITMAP m_DownBmp;
HBITMAP m_DisableBmp;

int m_InitDC;
int m_btnType;
bool m_bCheck;
// Construction
public:
CIconButton();
bool GetCheck();
void SetCheck(bool iCheck);

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CIconButton)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL

// Implementation
public:
void EnableWindow(bool);
void SetButType(int nType);
void LoadBitmaps(UINT nIDBitmapResource,
UINT nIDBitmapResourceSel = 0,
UINT nIDBitmapResourceFocus = 0,
UINT nIDBitmapResourceDisabled = 0 );
virtual ~CIconButton();

// Generated message map functions
protected:
//{{AFX_MSG(CIconButton)
afx_msg void OnPaint();
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

CIconButton::CIconButton()
{
m_InitDC = FALSE;
m_NormalDC = CreateCompatibleDC(NULL);
m_DisableDC =CreateCompatibleDC(NULL);
m_DownDC = CreateCompatibleDC(NULL);
m_btnType = BT_NORMAL;
m_NormalBmp = NULL;
m_DownBmp = NULL;
m_DisableBmp = NULL;
m_bCheck = 0;
}

CIconButton::~CIconButton()
{
// m_DisableDC.DeleteDC();
// m_DownDC.DeleteDC();
// m_NormalDC.DeleteDC();
//
// m_NormalBmp.DeleteObject();
// m_DownBmp.DeleteObject();
// m_DisableBmp.DeleteObject();
if(!DeleteDC(m_DisableDC))
{
RETAILMSG(1,(TEXT("Delete Disable DC Failed!\n")));
}
if(!DeleteDC(m_DownDC))
{
RETAILMSG(1,(TEXT("Delete Dowm DC Failed!\n")));
}
if(!DeleteDC(m_NormalDC))
{
RETAILMSG(1,(TEXT("Delete Normal DC Failed!\n")));
}

if (m_NormalBmp)
{
if(!DeleteObject(m_NormalBmp))
{

RETAILMSG(1,(TEXT("Delete Normal Bmp Failed!,ErrCode=%d\n"),GetLastError()));
}
}
if (m_DownBmp)
{
if(!DeleteObject(m_DownBmp))
{
RETAILMSG(1,(TEXT("Delete Down Bmp Failed!,ErrCode=%d\n"),GetLastError()));
}
}
if(m_DisableBmp)
{
if(!DeleteObject(m_DisableBmp))
{
RETAILMSG(1,(TEXT("Delete Disable Bmp Failed!,ErrCode=%d\n"),GetLastError()));
}
}

}



BEGIN_MESSAGE_MAP(CIconButton, CButton)
//{{AFX_MSG_MAP(CIconButton)
ON_WM_PAINT()
ON_WM_CTLCOLOR()
//ON_WM_LBUTTONDBLCLK()
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CIconButton message handlers

void CIconButton::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
// 在此设置按钮样式为自绘按钮
ModifyStyle(0, GetStyle()|BS_OWNERDRAW);

CButton::PreSubclassWindow();
}

void CIconButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your code to draw the specified item
CRect rect;
GetClientRect(&rect);
UINT status = lpDrawItemStruct->itemState;
//CClientDC dc(this);
if ( (status & ODS_DISABLED)&&(m_btnType & BT_DISABLE) )
{
BitBlt(lpDrawItemStruct->hDC,0,0,rect.Width(),rect.Height(),m_DisableDC,0,0,SRCCOPY);
}
else if ((status & ODS_SELECTED) ||m_bCheck)
{
BitBlt(lpDrawItemStruct->hDC,0,0,rect.Width(),rect.Height(),m_DownDC,0,0,SRCCOPY);
Sleep(30); //防止快速点击显示不全
}
else
{
BitBlt(lpDrawItemStruct->hDC,0,0,rect.Width(),rect.Height(),m_NormalDC,0,0,SRCCOPY);
}
}

void CIconButton::OnPaint()
{
CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here
if (m_InitDC)
{
CRect rect;
GetClientRect(&rect);

UINT status = GetState();

if ( (status & ODS_DISABLED)&&(m_btnType & BT_DISABLE) )
{
BitBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(),m_DisableDC,0,0,SRCCOPY);
}
else if ((status & ODS_SELECTED) ||m_bCheck)
{
BitBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(),m_DownDC,0,0,SRCCOPY);
Sleep(30); //防止快速点击显示不全
}
else
{
BitBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(),m_NormalDC,0,0,SRCCOPY);
}
}
// Do not call CButton::OnPaint() for painting messages
}



HBRUSH CIconButton::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
//HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO: Change any attributes of the DC here

// TODO: Return a different brush if the default is not desired
//return hbr;
//modify lft 2014.4.25 20:02
return (HBRUSH)GetStockObject(NULL_BRUSH); //返回一个透明刷子,
//HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

}

void CIconButton::LoadBitmaps(
UINT nIDBitmapResource,
UINT nIDBitmapResourceSel ,
UINT nIDBitmapResourceFocus ,
UINT nIDBitmapResourceDisabled )
{
// BITMAP TmpBmp;
if (m_NormalBmp )
DeleteObject(m_NormalBmp);

// CRect rect;
// GetClientRect(&rect);
m_NormalBmp = LOADDLLHBMP(nIDBitmapResource);
SelectObject(m_NormalDC,m_NormalBmp);

// m_NormalDC.SetBkColor(RGB(255,255,0));
// m_NormalDC.Rectangle(rect);

if (nIDBitmapResourceSel != 0)
{
if (m_DownBmp )
DeleteObject(m_DownBmp);
m_DownBmp = LOADDLLHBMP(nIDBitmapResourceSel);
SelectObject(m_DownDC,m_DownBmp);

// m_DownDC.SetBkColor(RGB(255,0,0));
// m_DownDC.Rectangle(rect);
m_btnType |= BT_DOWN;
}

if (nIDBitmapResourceDisabled != 0)
{
if (m_DisableBmp )
DeleteObject(m_DisableBmp);
m_DisableBmp = LOADDLLHBMP(nIDBitmapResourceDisabled);
SelectObject(m_DisableDC,m_DisableBmp);
m_btnType |= BT_DISABLE;
}
m_InitDC = TRUE;
}

void CIconButton::SetButType(int nType)
{
m_btnType |= nType;
}

void CIconButton::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
//SendMessage(WM_LBUTTONDOWN,0,0);
if(!bLockIconBtn)
{
CButton::OnLButtonDblClk(nFlags, point);
}

}

void CIconButton::OnLButtonDown(UINT nFlags, CPoint point)
{
//SendMessage(WM_LBUTTONDOWN,0,0);
if(!bLockIconBtn)
{
CButton::OnLButtonDown(nFlags,point);
}
}

void CIconButton::EnableWindow(bool bEnable)
{
int style=GetStyle(); //GetWindowLong(GWL_STYLE);
#define WS_DISABLE 0X08000000
if (bEnable)
{
style &= (~WS_DISABLE);
}
else
{
style |=WS_DISABLE;
}
::SetWindowLong(GetSafeHwnd( ),GWL_STYLE,style);
Invalidate();
}

bool CIconButton::GetCheck()
{
return m_bCheck;
}

void CIconButton::SetCheck(bool bCheck)
{
m_bCheck=bCheck;
Invalidate();
}
...全文
150 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
梁阿狸 2015-04-30
  • 打赏
  • 举报
回复
其实原因我也很纳闷,最后也没找到,我最后还是放弃了MFC,直接用win32做了。
  • 打赏
  • 举报
回复
双缓冲。。。。
梁阿狸 2015-04-16
  • 打赏
  • 举报
回复
引用 10 楼 accessysq 的回复:
刷新方式用redrawwindow,另外EraseBkgnd中必须删除你使用的dc,不然也会gdi泄漏。
即使删除dc也会出现泄漏。在OnPaint中实现不会出现泄漏的情况。但是使用OnPaint在界面切换时会出现闪烁。大神有没有什么解决的办法
91program 2015-04-15
  • 打赏
  • 举报
回复
SelectObject(m_NormalDC,m_NormalBmp); 是没有保存 OldObj,也就是说没有将 m_NormalBmp 从 DC 中选中,这样的话,Delete 时是无法成功删除它的。可以看 MSDN 中的说明,正在使用的 对象 是无法删除的。
  • 打赏
  • 举报
回复
刷新方式用redrawwindow,另外EraseBkgnd中必须删除你使用的dc,不然也会gdi泄漏。
梁阿狸 2015-04-15
  • 打赏
  • 举报
回复
引用 8 楼 91program 的回复:
[quote=引用 6 楼 accessysq 的回复:] [quote=引用 5 楼 oLiangALi 的回复:] [quote=引用 3 楼 accessysq 的回复:] 除了BZ说的问题,我给一点建议。 Invalidate();用redrawwindow代替。wince中有些情况Invalidate会导致内存泄漏,如果你要使用,那么很简单,用个循环多次调用SetCheck或者EnableWindow函数,然后看看内存是否4k的倍数增加就知道。
用GlobalMemoryStatus检测了一下,就算使用CButton,内存还是会泄漏,大概一分钟泄漏4KB.[/quote] 那你要排除是否真的是Cbutton引起的了,我的程序在界面上不停刷新,没遇到。界面上大概有20个按钮,还有其他控件10几个。 我的按钮类也是从Cbutton继承的。[/quote] 如果使用 CButton 类也会产生内存泄露,则需要分析一下其它代码,而不是 CIconButton 的实现。[/quote]谢谢!监测了一下,是重写OnEraseBkgnd函数引起的。就算这个函数直接return TRUE;还是会泄漏。但是不用EraseBkgnd画出来的又会闪烁。已经用双缓存在绘图了。刷新方式是直接InvalidateRect
91program 2015-04-15
  • 打赏
  • 举报
回复
引用 6 楼 accessysq 的回复:
[quote=引用 5 楼 oLiangALi 的回复:] [quote=引用 3 楼 accessysq 的回复:] 除了BZ说的问题,我给一点建议。 Invalidate();用redrawwindow代替。wince中有些情况Invalidate会导致内存泄漏,如果你要使用,那么很简单,用个循环多次调用SetCheck或者EnableWindow函数,然后看看内存是否4k的倍数增加就知道。
用GlobalMemoryStatus检测了一下,就算使用CButton,内存还是会泄漏,大概一分钟泄漏4KB.[/quote] 那你要排除是否真的是Cbutton引起的了,我的程序在界面上不停刷新,没遇到。界面上大概有20个按钮,还有其他控件10几个。 我的按钮类也是从Cbutton继承的。[/quote] 如果使用 CButton 类也会产生内存泄露,则需要分析一下其它代码,而不是 CIconButton 的实现。
91program 2015-04-15
  • 打赏
  • 举报
回复
引用 4 楼 oLiangALi 的回复:
引用 1 楼 91program 的回复:
SelectObject(m_NormalDC,m_NormalBmp); 是没有保存 OldObj,也就是说没有将 m_NormalBmp 从 DC 中选中,这样的话,Delete 时是无法成功删除它的。可以看 MSDN 中的说明,正在使用的 对象 是无法删除的。
删除的时候是先删除的DC,这个时候的HBITMAP是没有在使用的,这样的话是可以成功删除HBITMAP的,最少我没有看到输出删除HBITMAP的打印信息出来。
我给你的建议:先 Delete Bmp,再 Delete DC。否则,就算你看到成功,也是 API 骗你的。 还有,创建与删除的顺序需要注意,先创建、后删除(堆栈)。否则,会产生内存碎片,看起来也像是内存泄露。
  • 打赏
  • 举报
回复
引用 5 楼 oLiangALi 的回复:
[quote=引用 3 楼 accessysq 的回复:] 除了BZ说的问题,我给一点建议。 Invalidate();用redrawwindow代替。wince中有些情况Invalidate会导致内存泄漏,如果你要使用,那么很简单,用个循环多次调用SetCheck或者EnableWindow函数,然后看看内存是否4k的倍数增加就知道。
用GlobalMemoryStatus检测了一下,就算使用CButton,内存还是会泄漏,大概一分钟泄漏4KB.[/quote] 那你要排除是否真的是Cbutton引起的了,我的程序在界面上不停刷新,没遇到。界面上大概有20个按钮,还有其他控件10几个。 我的按钮类也是从Cbutton继承的。
梁阿狸 2015-04-15
  • 打赏
  • 举报
回复
引用 3 楼 accessysq 的回复:
除了BZ说的问题,我给一点建议。 Invalidate();用redrawwindow代替。wince中有些情况Invalidate会导致内存泄漏,如果你要使用,那么很简单,用个循环多次调用SetCheck或者EnableWindow函数,然后看看内存是否4k的倍数增加就知道。
用GlobalMemoryStatus检测了一下,就算使用CButton,内存还是会泄漏,大概一分钟泄漏4KB.
梁阿狸 2015-04-15
  • 打赏
  • 举报
回复
引用 1 楼 91program 的回复:
SelectObject(m_NormalDC,m_NormalBmp); 是没有保存 OldObj,也就是说没有将 m_NormalBmp 从 DC 中选中,这样的话,Delete 时是无法成功删除它的。可以看 MSDN 中的说明,正在使用的 对象 是无法删除的。
删除的时候是先删除的DC,这个时候的HBITMAP是没有在使用的,这样的话是可以成功删除HBITMAP的,最少我没有看到输出删除HBITMAP的打印信息出来。
  • 打赏
  • 举报
回复
除了BZ说的问题,我给一点建议。 Invalidate();用redrawwindow代替。wince中有些情况Invalidate会导致内存泄漏,如果你要使用,那么很简单,用个循环多次调用SetCheck或者EnableWindow函数,然后看看内存是否4k的倍数增加就知道。
  • 打赏
  • 举报
回复
除了BZ说的问题,我给一点建议。 Invalidate();用redrawwindow代替。wince中有些情况Invalidate会导致内存泄漏,如果你要使用,那么很简单,用个循环多次调用SetCheck或者EnableWindow函数,然后看看内存是否4k的倍数增加就知道。

19,502

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 嵌入开发(WinCE)
社区管理员
  • 嵌入开发(WinCE)社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧