Jump to content
  • Advertisement
Sign in to follow this  

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

This topic is 2685 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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
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 Save() 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
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
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!