当前位置: 代码迷 >> WinCE >> MFC自绘按钮内存泄漏解决方案
  详细解决方案

MFC自绘按钮内存泄漏解决方案

热度:436   发布时间:2016-04-28 11:52:31.0
MFC自绘按钮内存泄漏
把对应的按钮替换成系统默认的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();

------解决思路----------------------
SelectObject(m_NormalDC,m_NormalBmp); 是没有保存 OldObj,也就是说没有将 m_NormalBmp 从 DC 中选中,这样的话,Delete 时是无法成功删除它的。可以看 MSDN 中的说明,正在使用的 对象 是无法删除的。
------解决思路----------------------
除了BZ说的问题,我给一点建议。
Invalidate();用redrawwindow代替。wince中有些情况Invalidate会导致内存泄漏,如果你要使用,那么很简单,用个循环多次调用SetCheck或者EnableWindow函数,然后看看内存是否4k的倍数增加就知道。
  相关解决方案