您的位置:首页 > 其它

MFC中的DC,CDC和HDC

2017-06-30 18:01 567 查看
解决MFC闪烁的CMemDC类
MFC中的DC,CDC和HDC 
MFC的运行机制 以及 MFC中的DC、CDC、HDC、句柄、设备上下文(整理)
CmemDC类 的使用方法

DC:设备上下文(设备描述表),是WINDOWS的一种数据类型。

HDC:设备上下文句柄(可以理解为指向DC结构的指针),它指向一块描述设备的相关的内容的内存块。

CDC:是MFC里面的一个类,且这类封装了几乎所有关于HDC的操作,类的内部包含一个m_hWnd的句柄

一、内存泄漏相关

1.凡是Get出来的DC,如GetDC、GetWindowDC 等等,释放的时候要用 RelaseDC

2.凡是new出来的DC,如CreateDC、CreateCompatibleDC等等,释放的时候要用 DeleteDC

3.Create出来的gdi对象,一定要用DeleteObject来释放,释放顺序是先Create的后释放,后Create的先释放.

这里的Create指的是以它为开头的gdi函数,比如,CreateDIBitmap,CreateFont等等,最后都要调用DeleteObject来释放.

4.Create出来的dc要用DeleteDC来释放,Get到的要用ReleaseDC释放.

5.确保释放DC的时候DC中的各gdi对象都不是你自己创建的;确保个gdi对象在释放的时候不被任何dc选中使用.

 CPaintDC        无效区dc,相当于BeginPaint,    EndPaint   

 CClientDC       客户区dc,相当于GetDC,         ReleaseDC   

 CWindowDC   整窗口dc, 相当于GetWindowDC,   ReleaseDC   

 CDC                  任何dc,   相当于CreateDC,      DeleteDC

二、获取DC相关

CDC *pDC=GetDC();

得到控件的DC要下面的代码:

CWnd *pWnd=GetDlgItem(IDC_BUTTON); //控件ID

CRect rect;

pWnd->GetClientRect(&rect);

CDC *pDC=pWnd->GetDC();

或者
CDC * pDC;

pDC = GetDlgItem(IDC_BUTTON)->GetDC();  // 获取控件DC

 

三、HDC和CDC相互转换:

1、HDC到CDC的转换:

方法一: 此方法在设备结束时不会销毁原来的资源(即:hDC,hBitmap)
CDC *pDC = CDC::FromHandle(hDC);

CBitmap *pBitmap = CBitmap::FromHandle(hBitmap);

方法二:此方法在设备结束时会销毁原来的资源(即:hDC,hBitmap)
CDC dc;

dc.Attach(hDC);

CBitmap bit;

bit.Attach(hBitmap);

2、CDC到HDC的转换:

方法一

   CDC  dc;

   HDC  hDC;

   hDC = dc.GetSafeHdc();

方法二

   hDC = dc.m_hDC;

方法三
  CDC* pDC=GetDC();

 HDC hdc = pDC->GetSafeHdc();



3、首先:CDC 不可以释放。 FromHandle 是通过 HDC 来创建了一个CDC 对象,以方便操作,释放 DC 的操作应该针对于HDC 而非此CDC , 如果释放了它   pDC->ReleaseDC,就会造成隐患。 

      HDC   hDC   =   GetDC(hWnd);  

      CDC   *pDC  
bad3
=   CDC::FromHandle(hDC);   

      这两者是指向的一个DC对象,只能释放一次,而这个释放应该针对 hDC 而非 pDC。

   然后:GetDC和ReleaseDC的调用匹配,CreateDC和DeleteDC的调用匹配。GetDC是从窗口获取現有的DC,而CreateDC是创建DC,所以ReleaseDC和DeleteDC的作用一个是释放,一个是销毁。

四、CmemDC类 的使用方法

CMemDC其实就是对内存DC的创建与删除进行一个包装。
1、在CMemDC的构造函数中创建内存DC;

2、用户可以在CMemDC dc中进行图像的绘制;

3、在CMemDC的析构函数中进行内存DC到目标DC的拷贝,并做相应的GDI对象清理工作。

使用这样的类可以让你的代码有很大程度的简洁。

举个例子:

如果我们不使用CMemDC,一般我们会写下面的一段的代码,
CView::OnDraw(CDC* pDC)
{
CRect rcClient;
GetClientRect(&rcClient);
CDC dcMem;
dcMem.CreateCompatibleDC(pDC);
CBitmap bmpMem;
bmpMem.CreateCompatibleBitmap(pDC,rcClient.Width(),rcClient.Height());
CBitmap *pBmpOld = dcMem.SelectObject(&bmpMem);

//下面进行图像的绘制
dcMem.DrawText();
dcMem.FillSolidRect();

pDC->BitBlt(rcClient.left,rcClient.top,rcClient.Width(),rcClient.Height(),
&dcMem,0,0,SRCCOPY);

//GDI对象的清理
dcMem.SelectObject(pBmpOld);
bmpMem.DeleteObject();
dcMem.DeleteDC();
}


如果我们使用了CMemDC,那么代码就可以这样写,简洁了很多,更便于维护而且不容易出错。
CView::OnDraw(CDC* pDC)
{
CMemDC dcMem(pDC);
//下面进行图像的绘制
dcMem.DrawText();
dcMem.FillSolidRect();
}


一般不用自己写个类,不过有封装更好.

简单来说,步骤这样:(比如你真正的使用CDC *dc来绘制)

1.建立dc的内存DC

m_MemDc.CreateCompatibleDC(dc);

2.创建画布

m_bmp.CreateCompatibleBitmap(dc,宽度,高度);

3.选择画布

m_oldBmp = m_MemDc.SelectObject(&m_bmp);

4.内存Dc绘制东西

...

...

绘制自己需要的东西

5.使用真正绘图的dc拷贝内存dc的内容

dc.BitBlt(left,top,宽度,高度,&m_MemDc,m_MemDc中的起始X,m_MemDc中的起始y,SRCCOPY)

6.最后这个内存DC释放掉

m_MemDc.DeleteDC(); 

CMemDC类 其主要功能其实就是提供一个内存DC用于绘制,用于消除绘制时的闪烁,即双缓存机制。

一般来说,我们将将要显示的图首先绘制在内存DC上,然后在要显示的时候整个更新到前台DC上(使用BitBlt)。

我们首先来看一下CMemDC类的数据成员

private:  

  CBitmap  m_bitmap;  // Offscreen bitmap

  CBitmap*  m_oldBitmap;  // bitmap originally found in CMemDC

  CDC*    m_pDC;  // Saves CDC passed in constructor

  CRect    m_rect;  // Rectangle of drawing area.

  BOOL    m_bMemDC;  // TRUE if CDC really is a Memory DC.

可以看到这当中有两个重要的元素:m_bitmap和m_pDC。打个也许不大恰当的比方,

m_pDC指向的CDC对象好比是一张画板,我们将画画在上面,画好的画就是m_bitmap。

当我们将内存DC用BitBlt到前台DC的时候,正好就是将刚刚画的画显示到前台。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: