C++ 实现屏幕截图的方法

全屏截图的代码保存为BMP文件太大,以下代码整成了PNG截图,现在分享出来。

MakePNG.h

//MakePNG.h

#pragma once
#include <GdiPlus.h>
using namespace Gdiplus;
#pragma comment(lib,"GdiPlus.lib") 

class CMakePNG
{
public:
CMakePNG(void);
~CMakePNG(void);

BOOL MakePNG(HDC hDC,CRect rect,CString strFilePath);
BOOL BMptoPNG(LPCWSTR StrBMp,LPCWSTR StrPNG);
BOOL PNGtoBMp(LPCWSTR StrPNG,LPCWSTR StrBMp);
BOOL GetEncoderClsid(WCHAR* pFormat,CLSID* pClsid);
private:
GdiplusStartupInput m_gdiplusStartupInput;
ULONG_PTR m_pGdiToken;
};

MakePNG.cpp

//MakePNG.cpp

#include "StdAfx.h"
#include "MakePNG.h"

CMakePNG::CMakePNG(void)
{
GdiplusStartup(&m_pGdiToken,&m_gdiplusStartupInput,NULL);
}

CMakePNG::~CMakePNG(void)
{
}

/***************************************************************************/
/* 功能:  根据rect屏幕抓图,保存为文件名为strFilePath的PNG图像文件   */
/* 输入参数: HDC hDC     屏幕HDC;                          */
/*    CRect rect    需要的矩形;                        */
/*    CString strFilePath  保存文件全路径(含后缀名);        */
/***************************************************************************/
BOOL CMakePNG::MakePNG(HDC hDC, CRect rect, CString strFilePath)
{
BITMAP bmp;
PBITMAPINFO pbmi; 
PBITMAPINFOHEADER pbih;     // bitmap info-header 
BITMAPFILEHEADER  hdr;      // bitmap file-header
WORD    cClrBits; 
LPBYTE lpBits;              // memory pointer
DWORD dwTmp; 
DWORD cb;                   // incremental count of bytes 
BYTE *hp;                   // byte pointer 
HANDLE hfile;               // file handle 
CString szBMPFilename = strFilePath.Left(strFilePath.GetLength() - 3) + _T("bmp");//先保存成位图
HDC hdcCompatible = CreateCompatibleDC(hDC);
HBITMAP hbmScreen = CreateCompatibleBitmap(hDC, rect.Width(), rect.Height());

if (hbmScreen == NULL)
{
   AfxMessageBox(_T("CreateCompatibleBitmap() error")); 
return FALSE;
}

// Select the bitmaps into the compatible DC. 

if (!SelectObject(hdcCompatible, hbmScreen)) 
{
AfxMessageBox(_T("Compatible Bitmap Selection error")); 
return FALSE;
}

//Copy color data for the entire display into a 
//bitmap that is selected into a compatible DC. 

if (!BitBlt(hdcCompatible, 
0,0, 
rect.Width(), rect.Height(), 
hDC, 
rect.left,rect.top, 
SRCCOPY)) 
{
      AfxMessageBox(_T("Screen to Compat Blt Failed"));
return FALSE;
}



// Retrieve the bitmap's color format, width, and height. 
if (!GetObject(hbmScreen, sizeof(BITMAP), (LPSTR)&bmp))
{
      AfxMessageBox(_T("GetObject()出错!"));
return FALSE;
}
// Convert the color format to a count of bits. 
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); 
if (cClrBits == 1) 
cClrBits = 1; 
else if (cClrBits <= 4) 
cClrBits = 4; 
else if (cClrBits <= 8) 
cClrBits = 8; 
else if (cClrBits <= 16) 
cClrBits = 16; 
else if (cClrBits <= 24) 
cClrBits = 24; 
else cClrBits = 32; 

// Allocate memory for the BITMAPINFO structure. (This structure 
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD 
// data structures.) 

if (cClrBits != 24) 
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
sizeof(BITMAPINFOHEADER) + 
sizeof(RGBQUAD) * (1<< cClrBits)); 

// There is no RGBQUAD array for the 24-bit-per-pixel format. 

else 
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
sizeof(BITMAPINFOHEADER)); 

// Initialize the fields in the BITMAPINFO structure. 

pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
pbmi->bmiHeader.biWidth = bmp.bmWidth; 
pbmi->bmiHeader.biHeight = bmp.bmHeight; 
pbmi->bmiHeader.biPlanes = bmp.bmPlanes; 
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; 
if (cClrBits < 24) 
pbmi->bmiHeader.biClrUsed = (1<<cClrBits); 

// If the bitmap is not compressed, set the BI_RGB flag. 
pbmi->bmiHeader.biCompression = BI_RGB; 

// Compute the number of bytes in the array of color 
// indices and store the result in biSizeImage. 
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
* pbmi->bmiHeader.biHeight; 
// Set biClrImportant to 0, indicating that all of the device colors are important. 
pbmi->bmiHeader.biClrImportant = 0; 

pbih = (PBITMAPINFOHEADER) pbmi; 
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

if (!lpBits) 
{
   AfxMessageBox(_T("内存分配错误!"));
return FALSE;
}
// Retrieve the color table (RGBQUAD array) and the bits 
// (array of palette indices) from the DIB. 
if (!GetDIBits(hDC, hbmScreen, 0, (WORD) pbih->biHeight, lpBits, pbmi, 
DIB_RGB_COLORS)) 
{
AfxMessageBox(_T("GetDIBits() error"));
return FALSE;
}

// Create the .BMP file. 
hfile = CreateFile(szBMPFilename, 
GENERIC_READ | GENERIC_WRITE, 
(DWORD) 0, 
NULL, 
CREATE_ALWAYS, 
FILE_ATTRIBUTE_NORMAL, 
(HANDLE) NULL); 
if (hfile == INVALID_HANDLE_VALUE) 
{
 AfxMessageBox(_T("创建文件失败"));
return false;
}
hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M" 
// Compute the size of the entire file. 
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + 
pbih->biSize + pbih->biClrUsed 
* sizeof(RGBQUAD) + pbih->biSizeImage); 
hdr.bfReserved1 = 0; 
hdr.bfReserved2 = 0; 

// Compute the offset to the array of color indices. 
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + 
pbih->biSize + pbih->biClrUsed 
* sizeof (RGBQUAD); 

// Copy the BITMAPFILEHEADER into the .BMP file. 
if (!WriteFile(hfile, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), 
(LPDWORD) &dwTmp,  NULL)) 
{
 AfxMessageBox(_T("写BMP文件头失败"));
return FALSE;
}

// Copy the BITMAPINFOHEADER and RGBQUAD array into the file. 
if (!WriteFile(hfile, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) 
+ pbih->biClrUsed * sizeof (RGBQUAD), 
(LPDWORD) &dwTmp, ( NULL))) 
{
 AfxMessageBox(_T("写BMP文件头失败"));
return FALSE;
}

// Copy the array of color indices into the .BMP file. 
cb = pbih->biSizeImage; 
hp = lpBits; 
if (!WriteFile(hfile, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)) 
{
AfxMessageBox(_T("写入BMP文件失败"));
return FALSE;
}

// Close the .BMP file. 
if (!CloseHandle(hfile)) 
{
 AfxMessageBox(_T("Can't close BMP file.")); 
}

// Free memory. 
GlobalFree((HGLOBAL)lpBits);

//转换成PNG
if(!BMptoPNG(szBMPFilename,strFilePath))
{
DeleteFile(szBMPFilename);
return FALSE;
}
DeleteObject(hbmScreen);
DeleteFile(szBMPFilename);
return TRUE;
}
// //转换BMP文件为PNG文件            
BOOL CMakePNG::BMptoPNG(LPCWSTR StrBMp,LPCWSTR StrPNG)
{
CLSID encoderClsid;
Status stat;
Image* image = NULL;
image = Bitmap::FromFile(StrBMp,TRUE);
if (!GetEncoderClsid(L"image/png",&encoderClsid))
{
return FALSE;
}
stat = image->Save(StrPNG,&encoderClsid,NULL);
if (stat != Ok)
{
return FALSE;
}
delete image;
return TRUE;
}

// 功能描述:  转换PNG文件为BMP文件      
BOOL CMakePNG::PNGtoBMp(LPCWSTR StrPNG,LPCWSTR StrBMp)
{
CLSID encoderClsid;
Status stat;
Image* pImage;
pImage = Bitmap::FromFile(StrPNG,TRUE);
if (!GetEncoderClsid(L"image/bmp",&encoderClsid))
{
return FALSE;
}
stat = pImage->Save(StrBMp,&encoderClsid,NULL);
if (stat != Ok)
{
return FALSE;
}
delete pImage;
return TRUE;
}

BOOL CMakePNG::GetEncoderClsid(WCHAR* pFormat,CLSID* pClsid)
{
UINT num = 0,size = 0;
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num,&size);
if (size == 0)
{
return FALSE;
}
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
{
return FALSE;
}
GetImageEncoders(num,size,pImageCodecInfo);
BOOL bfound = FALSE;
for (UINT i = 0;!bfound && i < num;  i++)
{
if (_wcsicmp(pImageCodecInfo[i].MimeType,pFormat) == 0)
{
 *pClsid = pImageCodecInfo[i].Clsid;
 bfound = TRUE;
}
}
free(pImageCodecInfo);
return bfound;
}

以上两个文件实际上是CMakePNG类,使用时需要把他们添加到项目中,调用方法如下:

wstring GetAppPathW()
{
wchar_t szExePath[MAX_PATH] = {0};
GetModuleFileNameW(NULL, szExePath, MAX_PATH);
wchar_t *pstr = wcsrchr(szExePath, '\\');
memset(pstr + 1, 0, 2);
wstring strAppPath(szExePath);
return strAppPath;
}

// 屏幕截图
CString CDemoDlg::ScreenShot(void)
{
CWnd *pDesktop = GetDesktopWindow();  
CDC *pDC = pDesktop->GetDC();  
CRect rect;  
//获取窗口的大小  
pDesktop->GetClientRect(&rect);

//保存到的文件名
CString strFileName(GetAppPathW().c_str());
strFileName += _T("ScreenShot\\");
CreateDirectory((LPCTSTR)strFileName,NULL);
CTime t = CTime::GetCurrentTime();
CString tt = t.Format("%Y%m%d_%H%M%S");
strFileName += tt;
strFileName += _T(".PNG");
//保存为PNG
CMakePNG MakePNG;
MakePNG.MakePNG(pDC->m_hDC,rect,strFileName);
ReleaseDC(pDC);
return strFileName;
}

关于C++实现屏幕截图(全屏截图)的文章就介绍至此,更多相关C++ 屏幕截图内容请搜索编程宝库以前的文章,希望大家多多支持编程宝库

 1.结构体初始化结构体是常用的自定义构造类型,是一种很常见的数据打包方法。结构体对象的初始化有多种方式,分为顺序初始化、指定初始化、构造函数初始化。假如有如下结构体。struct A {int b ...