MFC编程-屏幕截图

时间:2021-06-10 15:26:03
问题一:怎么用VC进行屏幕截图?

答:在VC中进行屏幕截图可以分成几步:

(1) 获得整个屏幕的DC,并创建一个与之兼容的DC。

CDC *pDesktopDC=GetDesktopWindow()->GetDC();
 CDC memDC;
 memDC.CreateCompatibleDC(pDesktopDC);

(2)创建一个与屏幕大小相同的兼容位图。

CRect RectDesktop;
 GetDesktopWindow()->GetClientRect(&RectDesktop);
 CBitmap bmpdesktop;
 bmpdesktop.CreateCompatibleBitmap(pDesktopDC,RectDesktop.Width(),RectDesktop.Height());

(3)把位图显示在客户区。

 CRect rect;
 GetClientRect(&rect);
 CDC *pDC=this->GetDC();
 pDC->StretchBlt(0,0,rect.Width(),rect.Height(),pDesktopDC,0,0,RectDesktop.Width(),RectDesktop.Height(),SRCCOPY);
这样就能完成了简单截图的功能。

猜想:(1)按理来说呢,要想截取某个程序的图像,只要获得该程序的DC,并将之复制一份即可。
           (2)那么如果截取屏幕的某一区域的话,只需将区域范围保存下来,然后对复制的位图进行裁剪即可。

问题二:看下面的截图,大家是不是看着很别扭呢?这是使用上面的程序多次截图造成的。咋一看很吃惊,这不是我想要的结果,但事实确实如此。仔细想想也就能明白了。

MFC编程-屏幕截图

MFC编程-屏幕截图

那么怎么样才能不出现这种结果呢?我想应该是先清空客户区的图像,然后再截图。也就是在截图开始前添加清空客户区图像的功能。但是如果仅仅是要将背景图去掉的话,那也可以用背景色去填充整个客户区呀。
CRect rc;
GetClientRect(&rc);
pDC->Rectangle(&rc);
这样确实可以避免出现上面的窗口很多的情况。但是新问题又来了,这样的话每次截图都会闪屏。看来这个方法是行不通的,不能投机取巧啊!

前面讲了屏幕截图的一点知识,其实是很好懂的。如果我想要截取屏幕的部分区域怎么办呢?


下面就来讲一下。

    要保证截图的时候所有窗口都是无效的,这样在鼠标滑动的时候才不容易丢失焦点。可是这样办得到吗?至少我现在认为是不可能的,也许真的有某种方式可以做到。既然这样想行不通,那就换一个思路吧。我们可以采用欺骗的手段,先新建一个对话框,然后让它充满整个屏幕并且始终保持在所有窗口的最前面,然后将整个屏幕截取出来使之画满整个对话框窗口,然后记录鼠标在此图像内的截图区域,再传递给主窗口就可以了。这是我在网上找到的一个比较容易懂的方法。
    具体实现如下:
(1)新建一个对话框,设置属性为无标题,无边框,新建类CCutScreenAreaDlg,在初始化函数中将其设置为所有窗口最前。

int cxScreen,cyScreen;
 cxScreen=GetSystemMetrics(SM_CXSCREEN);
 cyScreen=GetSystemMetrics(SM_CYSCREEN);
 SetWindowPos(&wndTopMost,0,0,cxScreen,cyScreen,SWP_SHOWWINDOW);

(2)然后重载其WM_ERASEBKGND消息。

BOOL CCutScreenAreaDlg::OnEraseBkgnd(CDC* pDC) 
{
 // TODO: Add your message handler code here and/or call default
 CDC *pDesktopDC=GetDesktopWindow()->GetDC();
 CDC memDC;
 memDC.CreateCompatibleDC(pDesktopDC);
 CRect RectDesktop;
 GetDesktopWindow()->GetClientRect(&RectDesktop);
 CBitmap bmpdesktop;
 bmpdesktop.CreateCompatibleBitmap(pDesktopDC,RectDesktop.Width(),RectDesktop.Height());
 CRect rect;
 GetClientRect(&rect);
 pDC->StretchBlt(0,0,rect.Width(),rect.Height(),pDesktopDC,0,0,RectDesktop.Width(),RectDesktop.Height(),SRCCOPY);

 return TRUE;
}

(3)记录鼠标选中的区域,即左键按下为起点,右键按下为中点。

void CCutScreenAreaDlg::OnLButtonDown(UINT nFlags, CPoint point) 
{
 // TODO: Add your message handler code here and/or call default
 StartPoint = point;
  CDialog::OnLButtonDown(nFlags, point);
}

void CCutScreenAreaDlg::OnLButtonUp(UINT nFlags, CPoint point) 
{
 // TODO: Add your message handler code here and/or call default
 EndPoint = point;
  CDialog::OnLButtonUp(nFlags, point);
}

(4)在父窗口内截取图像,用到函数CutScreenArea(CRect Rect)。

void CMyView::CutScreenArea(CRect Rect)
{
 CDC *pDC=GetDC();
 CRect rc;
 CBrush brush;
 brush.CreateSolidBrush(RGB(255,255,255));
 GetClientRect(&rc);
 pDC->FillRect(&rc,&brush);

 CDC *pDesktopDC=GetDesktopWindow()->GetDC();
 CRect RectDesktop;
 GetDesktopWindow()->GetClientRect(&RectDesktop);
 CDC memDC;
 memDC.CreateCompatibleDC(pDesktopDC);
 CBitmap bmpdesktop;
 bmpdesktop.CreateCompatibleBitmap(pDesktopDC,RectDesktop.Width(),RectDesktop.Height());
 CRect rect;
 GetClientRect(&rect); 
 pDC->StretchBlt(0,0,rect.Width(),rect.Height(),pDesktopDC,Rect.left,
  Rect.top,Rect.Width(),Rect.Height(),SRCCOPY);
 grect = Rect;
}

(5)添加一个按钮消息响应函数OnCutscreenarea() 。

void CMyView::OnCutscreenarea() 
{
 // TODO: Add your command handler code here
 HCURSOR hCursor=AfxGetApp()->LoadCursor(IDC_CURSOR1);
 SetCursor(hCursor);

 CCutScreenAreaDlg dlg;
 if(IDOK == dlg.DoModal())
 {
  StartPoint = dlg.StartPoint;
  EndPoint = dlg.EndPoint;
  CRect rect;
  rect.left = StartPoint.x;
  rect.right = EndPoint.x;
  rect.bottom = EndPoint.y;
  rect.top = StartPoint.y;
  grect = rect;
  CutScreenArea(grect);
 }
}

经过以上步骤就能在客户区内显示截取的区域图像了。

下一次讲怎么保存图像。


我自己的技术还没到家,所以只讲怎么保存成bmp格式的位图文件。
一开始的时候我陷在整个屏幕截图的泥潭中,保存的图像总有好大一部分黑边。后来终于想明白了。你想截取哪个窗口的图像,就找到那个窗口的DC (设备上下文),然后创建与之兼容的DC,和与之大小的兼容位图,将DC复制到兼容的DC中,保存起来。陷在我想保存客户区内的图像,那我就找到客户区的DC,然后将之整个的保存起来。
详细代码如下:
////////////////////////////////////////////////////////////////////////////////////////
//拷贝客户区的DC内容到兼容DC
///////////////////////////////////////////////////////////////////////////////////////
CDC *pDC=GetDC();
 CRect rect;
 GetClientRect(&rect);
 CDC bmemDC;
 bmemDC.CreateCompatibleDC(pDC);
 CBitmap bmprect;
 bmprect.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());
 bmemDC.SelectObject(&bmprect);

 bmemDC.BitBlt(0,0,mbitmap.bmWidth,mbitmap.bmHeight,pDC,0,0,SRCCOPY);

/////////////////////////////////////////////////////////////////////////////////////////
//将位图信息填充到一个缓冲区内
////////////////////////////////////////////////////////////////////////////////////////
 BITMAP mbitmap;
 bmprect.GetBitmap(&mbitmap);
 BITMAPINFOHEADER bih = {0};//位图信息头
 bih.biBitCount = mbitmap.bmBitsPixel;//每个像素字节大小
 bih.biCompression = BI_RGB;
 bih.biHeight = mbitmap.bmHeight;//高度
 bih.biPlanes = 1;
 bih.biSize = sizeof(BITMAPINFOHEADER);
 bih.biSizeImage = mbitmap.bmWidthBytes * mbitmap.bmHeight;//图像数据大小
 bih.biWidth = mbitmap.bmWidth;//宽度
 
 BITMAPFILEHEADER bfh = {0};//位图文件头
 bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);//到位图数据的偏移量
 bfh.bfSize = bfh.bfOffBits + mbitmap.bmWidthBytes * mbitmap.bmHeight;//文件总的大小
 bfh.bfType = (WORD)0x4d42;
 byte * p = new byte[mbitmap.bmWidthBytes * mbitmap.bmHeight];//申请内存保存位图数据
 GetDIBits(bmemDC.m_hDC, (HBITMAP) bmprect.m_hObject, 0, rect.Height(), p,
  (LPBITMAPINFO) &bih, DIB_RGB_COLORS);//获取位图数据
  ///////////////////////////////////////////////////////////////////////////////////////////
//文件保存对话框,保存文件
//////////////////////////////////////////////////////////////////////////////////////////////
 CString strFileName,strszFilter;
 strszFilter="位图文件(*.bmp)|*.bmp|全部文件(*.*)|*.*||";
 CFileDialog bmpdlg(FALSE,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,strszFilter,NULL);
 if(IDOK == bmpdlg.DoModal())
 {
  strFileName = bmpdlg.GetFileName();
  strFileName +=".bmp";
  char filename[MAX_PATH];
  strcpy(filename,strFileName);
  FILE *fp = fopen(filename, "w+b");
  fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);//写入位图文件头 
  fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);//写入位图信息头
  fwrite(p, 1, mbitmap.bmWidthBytes * mbitmap.bmHeight, fp);//写入位图数据
  fclose(fp);
 }
  delete [] p;


转自lvan GoGo 的世界