Sign in to follow this  

C++ Win32 API: save screen to bmp (256 colors problem)

Recommended Posts

Texus    248
I hope that this is the right forum to ask this question.

I am trying to copy the screen and save it to a bmp file. The code does work, but not on a pc with 256 colors.

I tested the code on my Windows 7 and it works correctly.
When I tried this code on an older pc (Windows 2000) which has only desktop settings with 8-bit color, it failed.
I don't get any errors, but the image is corrupted. (I think that the file is too small: 768 kB for an image of 1024x768)

It is unimportant why, but I will not be able to debug the code on my older pc.
The only way to find out if everything works is to use messageboxes.
I have only tested one thing so far: cClrBits is 8 on my old pc.

Does anyone know why the code isn't working?
Are there perhaps functions in this code that don't work with 256 colors?

Thanks in advance.


// windows.h is included and _T(x) is defined here.
// The function prototype of WriteBMPFile is also here.


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)

int Width = GetSystemMetrics(SM_CXSCREEN);
int Height = GetSystemMetrics(SM_CYSCREEN);

HDC ScreenDC = GetDC(NULL);
HDC hDC = CreateCompatibleDC(ScreenDC);
HBITMAP hBitmap = CreateCompatibleBitmap(ScreenDC, Width, Height);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hDC, hBitmap);

if ( ! BitBlt(hDC, 0, 0, Width, Height, ScreenDC, 0, 0, SRCCOPY))
MessageBox(NULL, _T("BitBlt failed."), _T("PRINSCREEN PROGRAM: Error"), 0);

SelectObject(hDC, hOldBitmap);
ReleaseDC(NULL, ScreenDC);

WriteBMPFile(hBitmap, _T("SCREENSHOT.BMP"));

return 0;


void WriteBMPFile(HBITMAP bitmap, LPTSTR filename)
WORD cClrBits;
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;

// create the bitmapinfo header information

if (!GetObject( bitmap, sizeof(BITMAP), (LPSTR)&bmp))
MessageBox(NULL, _T("Could not retrieve bitmap info."), _T("PRINSCREEN PROGRAM: Error"), 0);

// 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.
if (cClrBits != 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));

// 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 + 7) / 8 * pbmi->bmiHeader.biHeight * cClrBits;

// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;

// now open file and save the data
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

if (!lpBits)
MessageBox(NULL, _T("Could not allocate memory."), _T("PRINSCREEN PROGRAM: Error"), 0);

HDC hDC = CreateCompatibleDC(NULL);

// Retrieve the color table (RGBQUAD array) and the bits
if (!GetDIBits(hDC, HBITMAP(bitmap), 0, (WORD) pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS)) // I also tested it with DIB_PAL_COLORS
MessageBox(NULL, _T("GetDIB error."), _T("PRINSCREEN PROGRAM: Error"), 0);

// Create the .BMP file.
MessageBox(NULL, _T("Could not create file for writing"), _T("Error"), 0);

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(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL))
MessageBox(NULL, _T("Could not write in to file (BITMAPFILEHEADER)."), _T("PRINSCREEN PROGRAM: Error"), 0);

// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), (LPDWORD) &dwTmp, ( NULL)))
MessageBox(NULL, _T("Could not write in to file (BITMAPINFOHEADER and RGBQUAD)."), _T("PRINSCREEN PROGRAM: Error"), 0);

// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp, NULL))
MessageBox(NULL, _T("Could not write in to file (array of color indices)."), _T("PRINSCREEN PROGRAM: Error"), 0);

// Close the .BMP file.
if (!CloseHandle(hf))
MessageBox(NULL, _T("Could not close file."), _T("PRINSCREEN PROGRAM: Error"), 0);

// Free memory.

Share this post

Link to post
Share on other sites
SiCrane    11839
Do you have any objections to using GDI+? If not, then you may want to use construct a GDI+ Bitmap object for your screenshot and use the [url=]Save()[/url] member function to save to a file instead. It'd be a lot less code, and it'd allow you to change to saving alternate file types easily.

Share this post

Link to post
Share on other sites
Texus    248
I never used GDI+ before, but I don't have any objections to using it.

I will try it and see if it works that way.

Thanks for your help.

Share this post

Link to post
Share on other sites
Texus    248
I was looking for information about GDI+ when I found the solution to my problem on this forum: [url=""][/url].
I tried the code and it worked on my old pc too.

Next time I will however try to use GDI+ when I have to do something.
Thanks again for your reply.

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this