void CSkinButton::DrawBitmap(CDC* dc, HBITMAP hbmp, RECT r, int DrawMode)
{
    if(DrawMode==2)
    {
FillWithBitmap(dc,hbmp,r);
return;
    }
   if(!hbmp) 
     return; //safe check   int cx=r.right  - r.left;
   int cy=r.bottom - r.top;
   HDC hdcBmp = NULL;
   HDC hdcMask = NULL;
   HDC hdcMem = NULL;
   HBITMAP hbmOldBmp = NULL;
   dcBmp.CreateCompatibleDC(dc);
//   hdcBmp = ::CreateCompatibleDC(dc->m_hDC);
   hdcBmp = ::CreateCompatibleDC(NULL);
   if(hdcBmp == NULL)
   {
       AfxMessageBox("CSkinButton::DrawBitmap()异常hdcBmp == NULL");
       int iErrorCode = GetLastError();
       LPVOID lpMsgBuf;
       FormatMessage( 
  FORMAT_MESSAGE_ALLOCATE_BUFFER | 
  FORMAT_MESSAGE_FROM_SYSTEM | 
  FORMAT_MESSAGE_IGNORE_INSERTS,
  NULL,
  iErrorCode, //错误代码
  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  (LPTSTR) &lpMsgBuf,
  0,
  NULL 
  );
       CString str;
       str.Format("%s,ErrorCode = %d",lpMsgBuf,iErrorCode);
       AfxMessageBox(str);
       LocalFree( lpMsgBuf );
       return;
    }
    hbmOldBmp = (HBITMAP)::SelectObject(hdcBmp,hbmp);

    if (m_bMask.m_hObject!=NULL)
    {
//       hdcMask = ::CreateCompatibleDC(dc->m_hDC);
       hdcMask = ::CreateCompatibleDC(NULL);
       if(hdcMask == NULL)
       {
 AfxMessageBox("CSkinButton::DrawBitmap()异常hdcMask == NULL");
 int iErrorCode = GetLastError();
 LPVOID lpMsgBuf;
 FormatMessage( 
         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
FORMAT_MESSAGE_FROM_SYSTEM | 
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
iErrorCode, //错误代码
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),           (LPTSTR) &lpMsgBuf,
0,
NULL 
);
CString str;
str.Format("%s,ErrorCode = %d",lpMsgBuf,iErrorCode);
AfxMessageBox(str);
LocalFree( lpMsgBuf );
return;
    }
    ::SelectObject(hdcMask,m_bMask);    hdcMem = ::CreateCompatibleDC(NULL);
    if(hdcMem == NULL)
    {
AfxMessageBox("CSkinButton::DrawBitmap()异常hdcMem == NULL");
int iErrorCode = GetLastError();
LPVOID lpMsgBuf;
FormatMessage( 
         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
FORMAT_MESSAGE_FROM_SYSTEM | 
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
iErrorCode, //错误代码
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL 
     );
CString str;
str.Format("%s,ErrorCode = %d",lpMsgBuf,iErrorCode);
AfxMessageBox(str);
LocalFree( lpMsgBuf );
return;
    }
    CBitmap hBitmap;
    hBitmap.CreateCompatibleBitmap(dc,cx,cy);
    ::SelectObject(hdcMem,hBitmap);    ::BitBlt(hdcMem,r.left,r.top,cx,cy,dc->m_hDC,0,0,SRCCOPY);
    if(!DrawMode)
    {
         ::BitBlt(hdcMem,r.left,r.top,cx,cy,hdcBmp,0,0,SRCINVERT);
::BitBlt(hdcMem,r.left,r.top,cx,cy,hdcMask,0,0,SRCAND);
::BitBlt(hdcMem,r.left,r.top,cx,cy,hdcBmp,0,0,SRCINVERT);
     } 
     else 
     {
int bx=GetBitmapWidth(hbmp);
int by=GetBitmapHeight(hbmp);
::StretchBlt(hdcMem,r.left,r.top,cx,cy,hdcBmp,0,0,bx,by,SRCINVERT);
::StretchBlt(hdcMem,r.left,r.top,cx,cy,hdcMask,0,0,bx,by,SRCAND);
::StretchBlt(hdcMem,r.left,r.top,cx,cy,hdcBmp,0,0,bx,by,SRCINVERT);
      }
      ::BitBlt(dc->m_hDC,r.left,r.top,cx,cy,hdcMem,0,0,SRCCOPY);      ::DeleteDC(hdcMem);
      hBitmap.DeleteObject();      ::DeleteDC(hdcMask);
  } 
  else 
  {
     if(!DrawMode)
     {
::BitBlt(dc->m_hDC,r.left,r.top,cx,cy,hdcBmp,0,0,SRCCOPY);
     } 
     else 
     {
int bx=GetBitmapWidth(hbmp);
int by=GetBitmapHeight(hbmp);
::StretchBlt(dc->m_hDC,r.left,r.top,cx,cy,hdcBmp,0,0,bx,by,SRCCOPY);
     }
   }
   ::DeleteDC(hdcBmp);
}

解决方案 »

  1.   

    这个函数是用来给多态按钮绘制贴图的..在CSkinButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 中被调用,第一个传入参数是CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);应该是没有问题的...我程序大量用到了这个类...并且有频繁的按钮状态转换操作...程序在跑了一段时间后(1-4小时不等)..就会弹出AfxMessageBox("CSkinButton::DrawBitmap()异常hdcBmp == NULL");
    这样的错误..也有可能是这个创建成功..但是后面两个DC没成功...也就是hdcBmp = ::CreateCompatibleDC(NULL);调用失败了...但是我代码里弹出的GetLastError又是0,提示操作成功完成...DeleteDC()也对应调用过了..我还用hdcBmp = ::CreateCompatibleDC(dc->m_hDC);试过也一样大家都来帮帮忙..解决了的话需要这个多态按钮类的朋友我可以发源码给你们..谢谢了
      

  2.   

    资源泄露吧   dcBmp.CreateCompatibleDC(dc);
    dcBmp没有删除,好像也没有用到:),去掉吧
      

  3.   

    有用到了.而且也正确删除了.if(!DrawMode)
        {
             ::BitBlt(hdcMem,r.left,r.top,cx,cy,hdcBmp,0,0,SRCINVERT);
    ::BitBlt(hdcMem,r.left,r.top,cx,cy,hdcMask,0,0,SRCAND);
    ::BitBlt(hdcMem,r.left,r.top,cx,cy,hdcBmp,0,0,SRCINVERT);
         } 
         else 
         {
    int bx=GetBitmapWidth(hbmp);
    int by=GetBitmapHeight(hbmp);
    ::StretchBlt(hdcMem,r.left,r.top,cx,cy,hdcBmp,0,0,bx,by,SRCINVERT);
    ::StretchBlt(hdcMem,r.left,r.top,cx,cy,hdcMask,0,0,bx,by,SRCAND);
    ::StretchBlt(hdcMem,r.left,r.top,cx,cy,hdcBmp,0,0,bx,by,SRCINVERT);
          }这里有用到hdcBmp::DeleteDC(hdcBmp);
    这里最后也删了主要奇怪的是能正常跑几个小时..然后出错
      

  4.   

    dcBmp没有用到啊,你用的是hdcBmp;两个不一样
      

  5.   

    晕倒.还是忘了注释.
    dcBmp.CreateCompatibleDC(dc);这句实际上已经被我注释掉了的,我编辑帖子的时候错了..
    我都没定义..编译都通不过的..
    不好意思..
    这里怎么编辑自己发过的帖子?
      

  6.   

    我仔细看了一遍原代码,是资源泄露问题,如果我没有猜错的话,你的::DeleteDC();函数都执行失败了.在deletedc之前,首先要确保dc中的hbitmap不是你自己创建的,也就是说,你要选入原来的hbitmap,这样才能执行deletedc,并且你没有释放所有create出来的hbitmap,这样也有资源泄露典型的,应该是这样写:hdcBmp = ::CreateCompatibleDC(NULL);                  //创建hdc/*这里需要创建hbmp,因为你的是从参数传过来的,所以不用写*/hbmOldBmp = (HBITMAP)::SelectObject(hdcBmp,hbmp);    //选入设备/*这里进行画图操作*/::SelectObject(hdcBmp,hbmOldBmp);                    //选出设备
    ::DeleteDC(hdcBmp);                                  //销毁hdc
    //::DeleteObject(hbmp);   //销毁hbmp,这个可以省略,因为你的程序
                              //里面这个hbmp不是你创建的
      

  7.   

    在deletedc之前,首先要确保dc中的hbitmap不是你自己创建的,也就是说,你要选入原来的hbitmap,这样才能执行deletedc,并且你没有释放所有create出来的hbitmap,这样也有资源泄露=========================================================
    有这个要求么?
      

  8.   

    应该不是bobob说的问题,俺经常这么干
      

  9.   

    在deletedc之前,首先要确保dc中的hbitmap不是你自己创建的,也就是说,你要选入原来的hbitmap,这样才能执行deletedc,并且你没有释放所有create出来的hbitmap,这样也有资源泄露=========================================================
    有这个要求么?==========================>好象微软并没有这样的建议,怪不得返回值是正确的^_^
    但是事实确实如此.如果少了这个动作,无论DeleteDC还是DeleteObject都会导致资源泄露.
    不相信的话,可以用第三方的检测工具检测一下
      

  10.   

    我按BOBOB的方法改了试下..晚点来报告结果...
      

  11.   

    dcBmp.DeleteDC();之后如果什么都不做当然会泄露
    但是之后如果把选入的Bmp,brush之类的东东统统DeleteOject一遍就不会泄露了
      

  12.   

    不行啊..改了后仍然是一两个小时报同样的错误..郁闷..
    这个错误有没有可能是程序其他地方Create了DC却没Delete造成的?
    或者说另外一个程序里有这种频繁出现的没Delete
      

  13.   

    CreateCompatibleDC()的返回值是一个HDC,你却赋值给一个HBITMAP能不失败吗
      

  14.   

    你最好从整个进程查找原因,我可以很确定的说,就是资源泄露造成的CreateCompatibleDC()执行失败,除了资源不够用,CreateCompatibleDC(NULL)几乎总能成功.如果程序比较大,这个原因可能并不好找,我建议你用boundschecker检测,它可以精确报告到资源或内存泄露的行
      

  15.   

    导致CreateCompatibleDC()执行失败的资源泄露是不是一定就是在程序其他地方同样调用了CreateCompatibleDC()但是没执行DeleteDC()??
    我用boundschecker编译程序后运行报的比较多的错误都是Reading uninitialized memory,这个好象关系不大吧.然后很奇怪的是报出的几个泄露错误都是底层的
    Memory leak:17 bytes allocated by operator new in strcore.cpp (118), HANDLE: 0x021B3050Resource leak: allocated by LoadLibraryA in dllinit.cpp (505), HANDLE: 0x61BE0000还有另外几个都是底层泄露,我都不知道是从哪进的.
    还有我特意在程序里的某个地方调用CreateCompatibleDC()后没调用DeleteDC(),但是boundschecker却没有捕捉到.晕倒了.
    我第一次用boundschecker,可能有些地方用的不对.请楼上的兄弟指点下
      

  16.   

    现在在用调式模式一直跑在..DEBUG窗口里不停的出这样的提示:
    First-chance exception in TDKClient.exe (KERNEL32.DLL): 0xC0000005: Access Violation.这是什么意思?
      

  17.   

    按照你的代码,应该会报资源错误的.
    如果你看到的是系统的文件错误,那么你就要查找到你自己使用的哪条语句调用到了系统文件.
    还有,一定要用debug版的跑,这样才能定位到你的程序里面
      

  18.   

    我就是用debug版跑的...boundschecker没有报错...但是
    DEBUG输出框里经常出现First-chance exception in TDKClient.exe (KERNEL32.DLL): 0xC0000005: Access Violation.又没办法跟踪进去看是哪里导致的...这是内存泄露的提示么?
      

  19.   

    我就是用debug版跑的...boundschecker没有报错...但是
    DEBUG输出框里经常出现First-chance exception in TDKClient.exe (KERNEL32.DLL): 0xC0000005: Access Violation.又没办法跟踪进去看是哪里导致的...这是内存泄露的提示么?
    =============================>
    单单从First-chance exception in TDKClient.exe (KERNEL32.DLL): 0xC0000005: Access Violation.不能判断是不是有问题我怀疑你可能没有正确使用boundschecker.先运行boundschecker,然后从boundschecker的菜单里面打开你的程序的debug版本,从boundschecker里面运行(就是说不用vc环境也可以,只要有debug版本的程序)
      

  20.   

    我是这样做的....仍然没有提示有资源泄露...
    但是经常有这个提示Reading uninitialized memory..CreateCompatibleDC()失败是不是总是API的资源泄露..和内存泄露没有关系?也就是说有地方new了内存没delete不会引起CreateCompatibleDC()的失败..
    只有没有DeleteDC()或者没有DeleteObject()才会导致??
      

  21.   

    发现有泄露了..但是不是在这个类里面..是在一个扩展的CRichEditCtrl里.
    class CFaceEdit : public CRichEditCtrl这个类是可以用来显示表情图象的Edit控件...是我从网上down的代码..其中下面的函数被频繁调用
    void CFaceEdit::InsertBitmap(stFace &face)
    {
    int nBegin = face.nPos;
    int nEnd = nBegin + face.nLength;
    SetSel(nBegin, nEnd); HBITMAP bmp = NULL;
    if(m_pIDBmp != NULL)
    //从资源创建HBITMAP
    bmp = ::LoadBitmap(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(m_pIDBmp[face.nFaceIndex]));
    else
    //从文件创建HBITMAP
    bmp = (HBITMAP)::LoadImage(NULL, m_pBmpFile[face.nFaceIndex], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); if(bmp==NULL)
    {
    throw "无效的HBITMAP类型。\r\n\r\n可能的原因是:\r\n位图文件的路径不正确。";
    } ::InsertBitmap(this, bmp); if(bmp != NULL)
    {
    int rs = DeleteObject(bmp);
    if(rs == 0)
    AfxMessageBox("Error DeleteObject(bmp)");//这里也会出现
    }
    }/*--------------------------------------------------------------------------
    * 函数名:InsertBitmap
    *
    * 功能  :底层的一个InsertBitmap,使用OLE容器,向CRichEditCtrl中插入表情。
    * 根据位图句柄创建OleCreateStaticFromData();用这个函数可以把资源中的图片插入到文本框中
    *
    * 头文件:<Richole.h>、<afxodlgs.h>
    --------------------------------------------------------------------------*/
    void InsertBitmap(CRichEditCtrl *pRichEdit, HBITMAP hBitmap) //底层的一个InsertBitmap
    {
    STGMEDIUM stgm;
    stgm.tymed = TYMED_GDI;    // Storage medium = HBITMAP handle
    stgm.hBitmap = hBitmap;
    stgm.pUnkForRelease = NULL; // Use ReleaseStgMedium

    FORMATETC fm;
    fm.cfFormat = CF_BITMAP;    // Clipboard format = CF_BITMAP
    fm.ptd = NULL;       // Target Device = Screen
    fm.dwAspect = DVASPECT_CONTENT;   // Level of detail = Full content
    fm.lindex = -1;       // Index = Not applicaple
    fm.tymed = TYMED_GDI;  

    //创建输入数据源
    IStorage *pStorage; 

    //分配内存
    LPLOCKBYTES lpLockBytes = NULL;
    SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
    if (sc != S_OK)
    AfxThrowOleException(sc);
    ASSERT(lpLockBytes != NULL);

    sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
    STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &pStorage);//内存泄露
    if (sc != S_OK)
    {
    VERIFY(lpLockBytes->Release() == 0);
    lpLockBytes = NULL;
    AfxThrowOleException(sc);
    AfxMessageBox("StgCreateDocfileOnILockBytes Error");
    }
    ASSERT(pStorage != NULL);

    COleDataSource *pDataSource = new COleDataSource;
    pDataSource->CacheData(CF_BITMAP, &stgm);
    LPDATAOBJECT lpDataObject = (LPDATAOBJECT)pDataSource->GetInterface(&IID_IDataObject);

    //获取RichEdit的OLEClientSite
    LPOLECLIENTSITE lpClientSite;
    pRichEdit->GetIRichEditOle()->GetClientSite( &lpClientSite );

    //创建OLE对象
    IOleObject *pOleObject;
    sc = OleCreateStaticFromData(lpDataObject,IID_IOleObject,OLERENDER_FORMAT,
    &fm,lpClientSite,pStorage,(void **)&pOleObject);//内存泄露
    if(sc!=S_OK)
    AfxThrowOleException(sc);

    //插入OLE对象
    REOBJECT reobject;
    ZeroMemory(&reobject, sizeof(REOBJECT));
    reobject.cbStruct = sizeof(REOBJECT);

    CLSID clsid;
    sc = pOleObject->GetUserClassID(&clsid);
    if (sc != S_OK)
    AfxThrowOleException(sc);

    reobject.clsid = clsid;
    reobject.cp = REO_CP_SELECTION;
    reobject.dvaspect = DVASPECT_CONTENT;
    reobject.poleobj = pOleObject;
    reobject.polesite = lpClientSite;
    reobject.pstg = pStorage;

    HRESULT hr = pRichEdit->GetIRichEditOle()->InsertObject( &reobject );
    int rs1 = lpLockBytes->Release();//2
    int rs2 = pStorage->Release();//3
    // lpDataObject->Release();
    int rs3 = pOleObject->Release();//1
    // CString str;
    // str.Format("%d %d %d",rs1,rs2,rs3);
    // AfxMessageBox(str);
    // delete pOleObject;
    // delete pStorage;
    pDataSource->Empty();
    delete pDataSource;}最后BoundChecker提示很多sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
    STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &pStorage);//内存泄露和sc = OleCreateStaticFromData(lpDataObject,IID_IOleObject,OLERENDER_FORMAT,
    &fm,lpClientSite,pStorage,(void **)&pOleObject);//内存泄露这两个地方有Interface leak..但是函数最后的确调用了Release()了然后在void CFaceEdit::InsertBitmap(stFace &face)的最后int rs = DeleteObject(bmp);
    返回0,也就是删除bmp失败了.求解啊.快晕头了
      

  22.   

    顶上去...现在基本上能确定就这个地方的泄露了....
    看解决方案也是说要调用Release()..
      

  23.   

    boundschecker是个什么东东阿?
    给我一个吧,[email protected]