VC++屏幕捕获并保存成图片(附源码)

目录

1、屏幕捕获(截取桌面)

2、将内存中的位图保存成图片文件

3、完整功能的屏幕截图


       我们可以将当前电脑屏幕上显示的内容截屏,并保存成图片文件。在视频会议软件中,桌面共享功能就是通过定时地捕获屏幕,经过视频编码后发出去的。

VC++屏幕捕获并保存成图片(附源码)_第1张图片

1、屏幕捕获(截取桌面)

      我们先获取桌面窗口句柄,桌面窗口中显示的内容,就是屏幕看到的内容。我们将桌面窗口中的内容拷贝到内存位图(用位图句柄标识)中,代码如下:

HBITMAP CopyScreenToBitmap(  ) 
{                           
	CString strLog;

	HWND hWndDeskTop = ::GetDesktopWindow();

	//HDC hScrDC = ::CreateDC( _T("DISPLAY"), NULL, NULL, NULL );
	HDC hScrDC = ::GetDC( hWndDeskTop ); // 为屏幕创建设备描述表
	if ( hScrDC == NULL )
	{
		strLog.Format( _T("[CopyScreenToBitmap] 创建DISPLAY失败, GetLastError: %d"), 
			GetLastError() );
		WriteLog( strLog );

		return NULL;
	}

	HDC hMemDC = ::CreateCompatibleDC( hScrDC ); // 为屏幕设备描述表创建兼容的内存设备描述表
	if ( hMemDC == NULL )
	{
		strLog.Format( _T("[CopyScreenToBitmap]创建与hScrDC兼容的hMemDC失败, GetLastError: %d"), 
			GetLastError() );
		WriteLog( strLog );

		//::DeleteDC( hScrDC );

		::ReleaseDC( hWndDeskTop, hScrDC );
		return NULL;
	}

	int nX = 0;
	int nY = 0;
	int nX2 = 0;
	int nY2 = 0;   
	int nWidth = 0; 
	int nHeight = 0;

	// 保证left小于right,top小于bottom
	CDirectRect rc = *lpRect;
	rc.Normalize();

	// 获得选定区域坐标
	nX = rc.left;
	nY = rc.top;
	nX2 = rc.right;
	nY2 = rc.bottom;

	// 确保选定区域是可见的
	if ( nX < 0 )
	{
		nX = 0;
	}

	if ( nY < 0 )
	{
		nY = 0;
	}

	if ( nX2 > m_xScreen )
	{
		nX2 = m_xScreen;
	}

	if ( nY2 > m_yScreen )
	{
		nY2 = m_yScreen;
	}

	nWidth = nX2 - nX;
	nHeight = nY2 - nY;

	HBITMAP hBitmap = ::CreateCompatibleBitmap( hScrDC, nWidth, nHeight ); // 创建一个与屏幕设备描述表兼容的位图
	::SelectObject( hMemDC, hBitmap ); 	// 把新位图选到内存设备描述表中

	BOOL bRet = ::BitBlt( hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY | CAPTUREBLT );  // CAPTUREBLT - 该参数保证能够截到透明窗口  
	if ( !bRet )
	{
		strLog.Format( _T("[CopyScreenToBitmap]将hScrDC拷贝到hMemDC失败, GetLastError: %d"), 
			GetLastError() );
		WriteLog( strLog );

		//::DeleteDC( hScrDC );
		::ReleaseDC( hWndDeskTop, hScrDC );
		::DeleteDC( hMemDC );
		::DeleteObject( hBitmap );
		return NULL;
	}

	if ( hScrDC != NULL )
	{
		//::DeleteDC( hScrDC );
		::ReleaseDC( hWndDeskTop, hScrDC );
	}

	if ( hMemDC != NULL )
	{
		::DeleteDC( hMemDC );
	}

	return hBitmap; // hBitmap资源不能释放,因为函数外部要使用
}

2、将内存中的位图保存成图片文件

       上面的CopyScreenToBitmap接口将捕获到的屏幕内容保存到hBitmap位图中,我们将该内存中的位图保存成图片:(接口中使用GDI+对象实现保存成图片文件的功能,支持多种图片类型)

// 获取jpeg等编码Clsid
int GetEncoderClsid( const WCHAR* format, CLSID* pClsid )   
{  
	UINT num = 0;                    // number of image encoders  
	UINT size = 0;                   // size of the image encoder array in bytes  
	ImageCodecInfo* pImageCodecInfo = NULL;  

	GetImageEncodersSize( &num, &size );  
	if( size == 0 ) 
	{
		return -1;     
	}

	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));  
	if( pImageCodecInfo == NULL )  
	{
		return -1;     
	}

	GetImageEncoders( num, size, pImageCodecInfo );  

	for( UINT j = 0; j < num; ++j )  
	{  
		if( wcscmp( pImageCodecInfo[j].MimeType, format ) == 0 )  
		{  
			*pClsid = pImageCodecInfo[j].Clsid;  
			free(pImageCodecInfo);  
			return j;     
		}          
	}  

	free( pImageCodecInfo );  

	return -1;     
}
// 使用gdi+将HBITMAP位图句柄图片资源保存成图片文件,图片文件的类型包括bmp、jpg、png、gif和tiff
// 根据lpfilename中包含的扩展名来判断是保存到哪个类型的文件中
BOOL SaveHBitmapToFile( HBITMAP hBmp, LPCTSTR lpfilename )
{
	if ( hBmp == NULL || lpfilename == NULL )
	{
		WriteLog( _T("[SaveHBitmapToFile]hBmp或lpfilename为空,return") );
		return FALSE;
	}

	CUIString strLog;
	int nLastErrCode = 0;
	Bitmap *pSrcBmp = Bitmap::FromHBITMAP( hBmp, NULL );
	if ( pSrcBmp == NULL )
	{
		nLastErrCode = ::GetLastError();
		strLog.Format( _T("[SaveHBitmapToFile]Bitmap::FromHBITMAP失败,GetLastError: %d"), 
			nLastErrCode );
		WriteLog( strLog );
		return FALSE;
	}

	// 获取文件的扩展名
	CUIString strExtName = _T("");
	CUIString strFilePatch = lpfilename;
	int nPos = strFilePatch.ReverseFind(_T('.'));
	if ( nPos != -1)
	{
		strExtName = strFilePatch.Right( strFilePatch.GetLength() - nPos - 1 );
	}

	// GDI+中有五种编码:bmp、jpeg、gif、tiff和png,下面根据
	// 文件名的扩展部分来获取对应的编码CLSID
	CLSID encoderClsid;
	if ( strExtName == _T("bmp") )
	{
		GetEncoderClsid( L"image/bmp", &encoderClsid );
	}
	else if ( strExtName == _T("jpg") )
	{
		GetEncoderClsid( L"image/jpeg", &encoderClsid );
	}
	else if ( strExtName == _T("png") )
	{
		GetEncoderClsid( L"image/png", &encoderClsid );
	}
	else if ( strExtName == _T("gif") )
	{
		GetEncoderClsid( L"image/gif", &encoderClsid );
	}
	else if ( strExtName == _T("tiff") )
	{
		GetEncoderClsid( L"image/tiff", &encoderClsid );
	}
	else // 没有指定类型的扩展名,则默认使用png
	{
		GetEncoderClsid( L"image/png", &encoderClsid );
	}

	LPWSTR lpWStrFileName = NULL; // 注意:Bitmap::Save接口第一个参数是宽字节参数
#ifdef _UNICODE
	lpWStrFileName = const_cast(lpfilename); // 如果是_UNICODE模式,直接使用
#else
	WCHAR wchFileName[MAX_PATH*2] = {0};
	MultiByteToWideChar( CP_ACP, 0, lpfilename, -1, wchFileName, sizeof(wchFileName)/sizeof(WCHAR) ); // 将窄字符转化为宽字符
	lpWStrFileName = wchFileName;
#endif

	Gdiplus::Status statusRet = pSrcBmp->Save( lpWStrFileName, &encoderClsid );
	if ( statusRet != Gdiplus::Ok )
	{
		nLastErrCode = ::GetLastError();
		strLog.Format( _T("[SaveHBitmapToFile]Bitmap::FromHBITMAP失败,,Gdiplus错误码:%d, GetLastError: %d"), 
			statusRet, nLastErrCode );
		WriteLog( strLog );

		// 释放内存
		delete pSrcBmp;
		return FALSE;
	}

	// 释放内存
	delete pSrcBmp;
	return TRUE;
}

3、完整功能的屏幕截图

      类似IM聊天软件中的完整功能的屏幕截图(包括桌面灰化、窗口自动套索、区域放大、绘制矩形等多个图元、输入文字等功能),可以查看本人的博文,

功能齐全的屏幕截图C++实现详解(附源码)https://blog.csdn.net/chenlycly/article/details/121197726?spm=1001.2014.3001.5502

你可能感兴趣的:(VC++常用功能代码封装,桌面屏幕,屏幕捕获,位图,图片)