Skip to content

Instantly share code, notes, and snippets.

@rdp
Created March 27, 2014 23:42
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save rdp/9821698 to your computer and use it in GitHub Desktop.
Save rdp/9821698 to your computer and use it in GitHub Desktop.
// 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;
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);
return ret;
}
main(){
ScreenCapture(500, 200, 300, 300, "testScreenCap.bmp");
printf("wrote to testScreenCap.bmp");
}
@pyed
Copy link

pyed commented Aug 10, 2015

running this code in a loop will leak memory, consider having the following main

main(){
  while (true){
      ScreenCapture(500, 200, 300, 300, "testScreenCap.bmp");
      printf("wrote to testScreenCap.bmp");
      Sleep(3000)
  }

any idea how to fix it ? I tried adding CloseHandle(BmpFile); before returning at line 142 but it didn't solve it.

@Awsom3D
Copy link

Awsom3D commented Feb 1, 2016

DeleteDC(hDc); is missing #memoryleak

@LBeckX
Copy link

LBeckX commented Mar 15, 2016

Hello,
i wrote DeleteDC(hDc); at line 165 but the memory is still full ...

I also do create for every run a new bitmap. So the memory is on 30000kb after three runs...

@LBeckX
Copy link

LBeckX commented Mar 16, 2016

Ok ... i found the prob.

The line 58 create new Array "lpvBits"

you have to delete it with: delete[] lpvBits; at line 141

@majimboo
Copy link

majimboo commented Oct 5, 2016

Does this still work with win 10?

@gaten
Copy link

gaten commented Jan 25, 2017

I'm using DevC++.
I have to include same library?
What library include?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment