Capture Screen or Screen Shot [C/C++ win32 api]

Reference:

I implemented screen shot from two methods, this post decription the way use [C/C++ win32 api] to get it, Python edition for screen shot is more easy than this, I’ll release it late also.

Just tips I got via this project’s developing!

  • Get Screen’s Resolution

    • method one

      INT width = GetSystemMetrics(SM_CXSCREEN);  // 屏幕宽
      INT height = GetSystemMetrics(SM_CYSCREEN); // 屏幕高
    • method two

      HDC hScreen = GetDC(NULL);
      RECT rect = { 0, 0, GetDeviceCaps(hScreen, HORZRES), GetDeviceCaps(hScreen, VERTRES) };
      POINT a, b;
      a.x = LPRECT(&rect)->left;
      a.y = LPRECT(&rect)->top;
      b.x = LPRECT(&rect)->right;
      b.y = LPRECT(&rect)->bottom;
  • Add #pragma comment(lib,"gdiplus.lib") to fix link error: error LNK2019: unresolved external symbol _GdipAlloc@4 etc.

  • Better way to connect string:

    #include <stdio.h>
    sprintf_s(filepath, "%s%s%s", "dirPath", "filename", ".bmp");
  • Convert CHAR* to WCHAR* (char* to wchar_t *):

    #include <stdio.h>
    wchar_t* wfilepath = new wchar_t[50];
    swprintf(wfilepath, 50, L"%hs", "filepath");

Capture Screen

One quick start

Refer to Screenshot C++ win32 api, it is the quick and easy way to see while or not you can capture a screen by program C. Just run and paste to Paint. You can get achivement quickly and easy to get how to shot screen.

Another way to get it (Method one)

Refer to https://gist.github.com/rdp/9821698
I follow the gist replies, and fixed memory leak resulted above:
NOTICE: Set properties in visule studio [Configuration]->[General]->[Character Set]->Use Multi-Byte Character Set

// compile this like g++ go2.c -lgdi32 [if you're using mingw]

#include <windows.h>
#include <stdio.h>

// Helper function to retrieve current position of file pointer:
inline int GetFilePointer(HANDLE FileHandle) {
return SetFilePointer(FileHandle, 0, 0, FILE_CURRENT);
}
//---------------------------------------------------------------------------

// Screenshot
// -> FileName: Name of file to save screenshot to
// -> lpDDS: DirectDraw surface to capture
// <- Result: Success
//
extern bool SaveBMPFile(char* filename, HBITMAP bitmap, HDC bitmapDC, int width, int height) {
bool Success = false;
HDC SurfDC = NULL; // GDI-compatible device context for the surface
HBITMAP OffscrBmp = NULL; // bitmap that is converted to a DIB
HDC OffscrDC = NULL; // offscreen DC that we can select OffscrBmp into
LPBITMAPINFO lpbi = NULL; // bitmap format info; used by GetDIBits
LPVOID lpvBits = NULL; // pointer to bitmap bits array
HANDLE BmpFile = INVALID_HANDLE_VALUE; // destination .bmp file
BITMAPFILEHEADER bmfh; // .bmp file header

// We need an HBITMAP to convert it to a DIB:
if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, width, height)) == NULL)
return false;

// The bitmap is empty, so let's copy the contents of the surface to it.
// For that we need to select it into a device context. We create one.
if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL)
return false;

// Select OffscrBmp into OffscrDC:
HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp);

// Now we can copy the contents of the surface to the offscreen bitmap:
BitBlt(OffscrDC, 0, 0, width, height, bitmapDC, 0, 0, SRCCOPY);

// GetDIBits requires format info about the bitmap. We can have GetDIBits
// fill a structure with that info if we pass a NULL pointer for lpvBits:
// Reserve memory for bitmap info (BITMAPINFOHEADER + largest possible
// palette):
if ((lpbi = (LPBITMAPINFO)(new char[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)])) == NULL)
return false;


ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER));
lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Get info but first de-select OffscrBmp because GetDIBits requires it:
SelectObject(OffscrDC, OldBmp);
if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, NULL, lpbi, DIB_RGB_COLORS))
return false;

// Reserve memory for bitmap bits:
if ((lpvBits = new char[lpbi->bmiHeader.biSizeImage]) == NULL)
return false;

// Have GetDIBits convert OffscrBmp to a DIB (device-independent bitmap):
if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, lpvBits, lpbi, DIB_RGB_COLORS))
return false;

// Create a file to save the DIB to:
if ((BmpFile = CreateFile(filename,
GENERIC_WRITE,
0, NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE)

return false;

DWORD Written; // number of bytes written by WriteFile

// Write a file header to the file:
bmfh.bfType = 19778; // 'BM'
// bmfh.bfSize = ??? // we'll write that later
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
// bmfh.bfOffBits = ??? // we'll write that later
if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL))
return false;

if (Written < sizeof(bmfh))
return false;

// Write BITMAPINFOHEADER to the file:
if (!WriteFile(BmpFile, &lpbi->bmiHeader, sizeof(BITMAPINFOHEADER), &Written, NULL))
return false;

if (Written < sizeof(BITMAPINFOHEADER))
return false;

// Calculate size of palette:
int PalEntries;
// 16-bit or 32-bit bitmaps require bit masks:
if (lpbi->bmiHeader.biCompression == BI_BITFIELDS)
PalEntries = 3;
else
// bitmap is palettized?
PalEntries = (lpbi->bmiHeader.biBitCount <= 8) ?
// 2^biBitCount palette entries max.:
(int)(1 << lpbi->bmiHeader.biBitCount)
// bitmap is TrueColor -> no palette:
: 0;
// If biClrUsed use only biClrUsed palette entries:
if (lpbi->bmiHeader.biClrUsed)
PalEntries = lpbi->bmiHeader.biClrUsed;

// Write palette to the file:
if (PalEntries) {
if (!WriteFile(BmpFile, &lpbi->bmiColors, PalEntries * sizeof(RGBQUAD), &Written, NULL))
return false;

if (Written < PalEntries * sizeof(RGBQUAD))
return false;
}

// The current position in the file (at the beginning of the bitmap bits)
// will be saved to the BITMAPFILEHEADER:
bmfh.bfOffBits = GetFilePointer(BmpFile);

// Write bitmap bits to the file:
if (!WriteFile(BmpFile, lpvBits, lpbi->bmiHeader.biSizeImage, &Written, NULL))
return false;

if (Written < lpbi->bmiHeader.biSizeImage)
return false;

// The current pos. in the file is the final file size and will be saved:
bmfh.bfSize = GetFilePointer(BmpFile);

// We have all the info for the file header. Save the updated version:
SetFilePointer(BmpFile, 0, 0, FILE_BEGIN);
if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL))
return false;

if (Written < sizeof(bmfh))
return false;
delete[] lpvBits;
return true;
}



bool ScreenCapture(int x, int y, int width, int height, char* filename) {
// get a DC compat. w/ the screen
HDC hDc = CreateCompatibleDC(0);

// make a bmp in memory to store the capture in
HBITMAP hBmp = CreateCompatibleBitmap(GetDC(0), width, height);

// join em up
SelectObject(hDc, hBmp);

// copy from the screen to my bitmap
BitBlt(hDc, 0, 0, width, height, GetDC(0), x, y, SRCCOPY);

// save my bitmap
bool ret = SaveBMPFile(filename, hBmp, hDc, width, height);

// free the bitmap memory
DeleteObject(hBmp);
DeleteDC(hDc);
return ret;
}

int main() {
char filename[100] = "testScreenCap.bmp";
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
bool ifSuccess = ScreenCapture(0, 0, width, height, filename);
if (ifSuccess)
{
printf("wrote to testScreenCap.bmp");
}
return 0;
}

Another way to get it (Method two)

NOTICE: Set [Configuration]->[General]->[Character Set]->Use Multi-Byte Character Set

#include <windows.h>

int CaptureImage(HWND hWnd, CHAR* dirPath, CHAR* filename);

int main()
{
CHAR path[100] = "D:\\";
CHAR filename[100] = "hello";
CaptureImage(GetDesktopWindow(), path, filename); // 保存为 E:hello.bmp
return 0;
}

/**
* GDI 截取指定窗口
*
* 参数 hwnd 要截屏的窗口句柄
* 参数 dirPath 截图存放目录
* 参数 filename 截图名称
*/
int CaptureImage(HWND hwnd, CHAR * dirPath, CHAR * filename)
{
HDC mdc;
HBITMAP hbmp;
CHAR FilePath[MAX_PATH];
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
RECT rcClient;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
DWORD dwBmpSize;
HANDLE hDIB;
CHAR* lpbitmap;
HANDLE hFile;
DWORD dwSizeofDIB;
DWORD dwBytesWritten;

hdcScreen = GetDC(NULL); // 全屏幕DC
hdcWindow = GetDC(hwnd); // 截图目标窗口DC

// 创建兼容内存DC
hdcMemDC = CreateCompatibleDC(hdcWindow);

if (!hdcMemDC)
{
goto done;
}

// 获取客户端区域用于计算大小
GetClientRect(hwnd, &rcClient);

// 设置延展模式
SetStretchBltMode(hdcWindow, HALFTONE);

// 来源 DC 是整个屏幕而目标 DC 是当前的窗口 (HWND)
if (!StretchBlt(hdcWindow,
0, 0,
rcClient.right, rcClient.bottom,
hdcScreen,
0, 0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SRCCOPY))
{
goto done;
}

// 通过窗口DC 创建一个兼容位图
hbmScreen = CreateCompatibleBitmap(
hdcWindow,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top
);

if (!hbmScreen)
{
goto done;
}

// 将位图块传送到我们兼容的内存DC中
SelectObject(hdcMemDC, hbmScreen);
if (!BitBlt(
hdcMemDC, // 目的DC
0, 0, // 目的DC的 x,y 坐标
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, // 目的 DC 的宽高
hdcWindow, // 来源DC
0, 0, // 来源DC的 x,y 坐标
SRCCOPY)) // 粘贴方式
{
goto done;
}

// 获取位图信息并存放在 bmpScreen 中
GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);

bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;

dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

// 在 32-bit Windows 系统上, GlobalAlloc 和 LocalAlloc 是由 HeapAlloc 封装来的
// handle 指向进程默认的堆. 所以开销比 HeapAlloc 要大
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);

// 获取兼容位图的位并且拷贝结果到一个 lpbitmap 中.
GetDIBits(
hdcWindow, // 设备环境句柄
hbmScreen, // 位图句柄
0, // 指定检索的第一个扫描线
(UINT)bmpScreen.bmHeight, // 指定检索的扫描线数
lpbitmap, // 指向用来检索位图数据的缓冲区的指针
(BITMAPINFO*)& bi, // 该结构体保存位图的数据格式
DIB_RGB_COLORS // 颜色表由红、绿、蓝(RGB)三个直接值构成
);


wsprintf(FilePath, "%s\%s.bmp", dirPath, filename);

// 创建一个文件来保存文件截图
hFile = CreateFile(
FilePath,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);

// 将 图片头(headers)的大小, 加上位图的大小来获得整个文件的大小
dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

// 设置 Offset 偏移至位图的位(bitmap bits)实际开始的地方
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);

// 文件大小
bmfHeader.bfSize = dwSizeofDIB;

// 位图的 bfType 必须是字符串 "BM"
bmfHeader.bfType = 0x4D42; //BM

dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)& bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)& bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

// 解锁堆内存并释放
GlobalUnlock(hDIB);
GlobalFree(hDIB);

// 关闭文件句柄
CloseHandle(hFile);

// 清理资源
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hwnd, hdcWindow);

return 0;
}

Convert BMP to JPG or PNG

Usefull cases:

if you wanna another format, exchange image/png to the list below:

  • image/bmp
  • image/jpeg
  • image/gif
  • image/tiff
  • image/png

Code for convert bmp to png:

#pragma comment(lib,"gdiplus.lib")
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;

INT GetEncoderClsid(const WCHAR* format, CLSID* pClsid); // helper function
INT convert2png();

// By reference:
// https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-converting-a-bmp-image-to-a-png-image-use
INT convert2png()
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

CLSID encoderClsid;
Status stat;
Image* image = new Image(L"hello.bmp");

// Get the CLSID of the PNG encoder.
GetEncoderClsid(L"image/png", &encoderClsid);
//GetEncoderClsid(L"image/jpeg", &encoderClsid);

stat = image->Save(L"hello.png", &encoderClsid, NULL);
//stat = image->Save(L"hello.jpg", &encoderClsid, NULL);

if (stat == Ok)
printf("hello.png was saved successfully\n");
else
printf("Failure: stat = %d\n", stat);

delete image;
GdiplusShutdown(gdiplusToken);
return 0;
}


// By reference:
// https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-retrieving-the-class-identifier-for-an-encoder-use
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; // Failure

pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure

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; // Success
}
}

free(pImageCodecInfo);
return -1; // Failure
}

My Final Result Code

Here are some of my insights about official documents:

  1. Official document are often useful, such as my searching for convert bmp to jpg, the shortest and best code from microsoft docs.
  2. Just refer inform posts, maybe it’s a quick way to resolve our problem, but, we can knowing it, but don’t know why, only officical document can tell you all if you patience enough.

For programe c/c++, for development environment differ, we also have to exhaust our effort to fix too many bugs, lost time for developing env, but not for developing business. So, share my env config here.

My Development environment configuration

  1. Target Platform Version: 8.1
  2. Platform Toolset: Visual Studio 2015 - Windows XP (v140_xp)
  3. Character Set: Use Multi-Byte Character Set

Complete code

You can save the code below as a .cpp file to run, it can catpure your screen and save hello.bmp and hello.png files respectively.
Good luck!

#pragma comment(lib,"gdiplus.lib")
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;

int CaptureImage(HWND hWnd, CHAR* dirPath, CHAR* filename);
INT GetEncoderClsid(const WCHAR* format, CLSID* pClsid); // helper function
INT convert2png();

int main()
{
CHAR path[100] = ".\\";
CHAR filename[100] = "hello";
CaptureImage(GetDesktopWindow(), path, filename); // 保存为 E:hello.bmp
convert2png();
return 0;
}


// By reference:
// https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-converting-a-bmp-image-to-a-png-image-use
INT convert2png()
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

CLSID encoderClsid;
Status stat;
Image* image = new Image(L"hello.bmp");

// Get the CLSID of the PNG encoder.
GetEncoderClsid(L"image/png", &encoderClsid);
//GetEncoderClsid(L"image/jpeg", &encoderClsid);

stat = image->Save(L"hello.png", &encoderClsid, NULL);
//stat = image->Save(L"hello.jpg", &encoderClsid, NULL);

if (stat == Ok)
printf("hello.png was saved successfully\n");
else
printf("Failure: stat = %d\n", stat);

delete image;
GdiplusShutdown(gdiplusToken);
return 0;
}


// By reference:
// https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-retrieving-the-class-identifier-for-an-encoder-use
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; // Failure

pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure

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; // Success
}
}

free(pImageCodecInfo);
return -1; // Failure
}

/**
* GDI 截取指定窗口
*
* 参数 hwnd 要截屏的窗口句柄
* 参数 dirPath 截图存放目录
* 参数 filename 截图名称
*/
int CaptureImage(HWND hwnd, CHAR* dirPath, CHAR* filename)
{
HDC mdc;
HBITMAP hbmp;
CHAR FilePath[MAX_PATH];
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
RECT rcClient;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
DWORD dwBmpSize;
HANDLE hDIB;
CHAR* lpbitmap;
HANDLE hFile;
DWORD dwSizeofDIB;
DWORD dwBytesWritten;

hdcScreen = GetDC(NULL); // 全屏幕DC
hdcWindow = GetDC(hwnd); // 截图目标窗口DC

// 创建兼容内存DC
hdcMemDC = CreateCompatibleDC(hdcWindow);

if (!hdcMemDC)
{
goto done;
}

// 获取客户端区域用于计算大小
GetClientRect(hwnd, &rcClient);

// 设置延展模式
SetStretchBltMode(hdcWindow, HALFTONE);

// 来源 DC 是整个屏幕而目标 DC 是当前的窗口 (HWND)
if (!StretchBlt(hdcWindow,
0, 0,
rcClient.right, rcClient.bottom,
hdcScreen,
0, 0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SRCCOPY))
{
goto done;
}

// 通过窗口DC 创建一个兼容位图
hbmScreen = CreateCompatibleBitmap(
hdcWindow,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top
);

if (!hbmScreen)
{
goto done;
}

// 将位图块传送到我们兼容的内存DC中
SelectObject(hdcMemDC, hbmScreen);
if (!BitBlt(
hdcMemDC, // 目的DC
0, 0, // 目的DC的 x,y 坐标
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, // 目的 DC 的宽高
hdcWindow, // 来源DC
0, 0, // 来源DC的 x,y 坐标
SRCCOPY)) // 粘贴方式
{
goto done;
}

// 获取位图信息并存放在 bmpScreen 中
GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);

bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;

dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

// 在 32-bit Windows 系统上, GlobalAlloc 和 LocalAlloc 是由 HeapAlloc 封装来的
// handle 指向进程默认的堆. 所以开销比 HeapAlloc 要大
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);

// 获取兼容位图的位并且拷贝结果到一个 lpbitmap 中.
GetDIBits(
hdcWindow, // 设备环境句柄
hbmScreen, // 位图句柄
0, // 指定检索的第一个扫描线
(UINT)bmpScreen.bmHeight, // 指定检索的扫描线数
lpbitmap, // 指向用来检索位图数据的缓冲区的指针
(BITMAPINFO*)& bi, // 该结构体保存位图的数据格式
DIB_RGB_COLORS // 颜色表由红、绿、蓝(RGB)三个直接值构成
);


wsprintf(FilePath, "%s\%s.bmp", dirPath, filename);

// 创建一个文件来保存文件截图
hFile = CreateFile(
FilePath,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);

// 将 图片头(headers)的大小, 加上位图的大小来获得整个文件的大小
dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

// 设置 Offset 偏移至位图的位(bitmap bits)实际开始的地方
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);

// 文件大小
bmfHeader.bfSize = dwSizeofDIB;

// 位图的 bfType 必须是字符串 "BM"
bmfHeader.bfType = 0x4D42; //BM

dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)& bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)& bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

// 解锁堆内存并释放
GlobalUnlock(hDIB);
GlobalFree(hDIB);

// 关闭文件句柄
CloseHandle(hFile);

// 清理资源
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hwnd, hdcWindow);

return 0;
}
0%