目录
1、屏幕捕获(截取桌面)
2、将内存中的位图保存成图片文件
3、完整功能的屏幕截图
我们可以将当前电脑屏幕上显示的内容截屏,并保存成图片文件。在视频会议软件中,桌面共享功能就是通过定时地捕获屏幕,经过视频编码后发出去的。
我们先获取桌面窗口句柄,桌面窗口中显示的内容,就是屏幕看到的内容。我们将桌面窗口中的内容拷贝到内存位图(用位图句柄标识)中,代码如下:
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资源不能释放,因为函数外部要使用
}
上面的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;
}
类似IM聊天软件中的完整功能的屏幕截图(包括桌面灰化、窗口自动套索、区域放大、绘制矩形等多个图元、输入文字等功能),可以查看本人的博文,
功能齐全的屏幕截图C++实现详解(附源码)https://blog.csdn.net/chenlycly/article/details/121197726?spm=1001.2014.3001.5502