Capturing a Window Image Into a Bitmap File, Supporting MS PaintBrush and All -- (Like the Thumbnail View of Windows Explorer)

Environment: VC++

Introduction

I have written this article to capture a Windows image into a bitmap file that will support all PaintBrush tools and Thumbnail Views of Windows Explorer. I have found many programmers suffering from this problem, including me, until I wrote this article.

The problem was that a Windows image is captured and also saved into a bitmap file. But, when it is opened with any paint tool, it will be seen as white only and also will not appear properly in the thumbnail view of Windows Explorer. But, after you save this bitmap file, using MS Paint or any other paint tool, it will open properly again as a common bitmap. Here, with this cool code, I have solved this problem. I hope it will be helpful to someone.

I also needed to capture an active window image and to save it into the ".bmp" file format. Therefore, I have written code to capture the window image and to save it into the BMP format.

Here, I used two functions, WndToBmpFile() and DDBToDIB(). Here is cool code for it.

About the Author

Hi! Guys,
I am not a well-experienced programmer but always try to do my best. So, that's why I have found this solution. I am working in an Indian Company as a senior programmer. We develop software and hardware related to video mixing and photography. My desire is to do a lot in the VC++ field especially in Graphics and Image Processing side. This is my beginning, so maybe you will not find many problems with my code. But, your appreciation will become an inspiration for me.

Code Compilation and Testing

I have compiled and tested well this code. So, don't bother about it!!

From:
Shailesh N. Kanzariya, shailesh_kanzariya@hotmail.com

//////////////////////////////////////////////////////////////////
Function Name :BOOL WndToBmpFile(CDC *pDC, CString szFile)
Parameters:
  CDC *pDC: is pointer to window DC whose image should be
    captured.
  CString szFile : is null terminated string with that name Bmp
    file should be saved.
//////////////////////////////////////////////////////////////////

//it will capture a wnd image and save it into a bmp file

BOOL WndToBmpFile(CDC *pDC, CString szFile)
{
//it will capture a wnd image and save it into a bmp file

  CString fname=szFile;
  CBitmap bmp,*pOldBmp;
  CRect rect;
  CWnd *pWnd;
  BOOL flg=0;
  CPalette pal;
  LOGPALETTE *pLp;

  if(pDC==NULL)               //if pDC is NULL return
    {
    return FALSE;
    }

  pWnd=pDC->GetWindow();      //Get Window of PDC

  pWnd->GetClientRect(&rect); //Get dimension of Window

  if(fname.IsEmpty())
    return FALSE;

  CDC memdc;

  memdc.CreateCompatibleDC(pDC);  //Make Compatible DC for memdc
  bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());
    //Create Compatible DDB
  pOldBmp=memdc.SelectObject(&bmp);
  memdc.BitBlt(0,0,rect.Width(),rect.Height(),pDC,0,0,SRCCOPY);

//The following code will detect whether the BMP uses a Raster
//palette or not.

  if(pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
  {
    int nSize;
    nSize=sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * 256;
    pLp=(LOGPALETTE*) new BYTE[nSize];
    pLp->palVersion=0x300;
    pLp->palNumEntries=GetSystemPaletteEntries(
         pDC->m_hDC,0,255,pLp->palPalEntry);
    pal.CreatePalette(pLp);

    delete [] pLp;
  }
  memdc.SelectObject(pOldBmp);

//will convert bitmap from DDB to DIB see DDBToDIB()
// See DDBToDIB function for more..

  HANDLE hDIB=DDBToDIB(bmp,BI_RGB,&pal);
  if(hDIB==NULL)
    return FALSE;

//*************************************
//This code writes the BMP file

  CFile m_file;
  if(!m_file.Open(fname,CFile::modeWrite |
                  CFile::modeCreate,NULL))
    return FALSE;
  BITMAPFILEHEADER hdr;
  LPBITMAPINFOHEADER lpbi;
  lpbi=(LPBITMAPINFOHEADER ) hDIB;
  int nColors= 1 << lpbi->biBitCount;
  hdr.bfType= ((WORD) ('M' << 8) | 'B');
  hdr.bfSize=sizeof(hdr) + GlobalSize(hDIB);
  hdr.bfReserved1=0;
  hdr.bfReserved2=0;
  hdr.bfOffBits=(DWORD) sizeof(hdr) + nColors * sizeof(RGBQUAD);
  m_file.Write(&hdr,sizeof(hdr));
  m_file.Write(lpbi,GlobalSize(hDIB));
  m_file.Close();
//**************************************

//This is the tricky part of the code. It will open the BMP file
//again, but in Binary Mode. Then, it will read the first 14
//bytes from the bitmap file.
//It will change the 11th byte from 11 to 36.
//It will change the 14th byte from 4 to 0 because this is the
//basic requirement for the bitmap format.
//So, it will support all PaintBrush Tools and thumbnail views
//of Windows Explorer.

  CBinFile  m_tempFile;    //CBinFile is derived from CFile
  BYTE dummy=0;//14        //14
  BYTE pBuf[14];           //11
  
  BOOL fres=m_tempFile.Open(fname,CFile::modeReadWrite |
                                  CFile::typeBinary);

  if(fres==0)
    return FALSE;

  UINT tt=m_tempFile.Read(pBuf,14);
  pBuf[13]=dummy;//will replace from 04 to 00
  m_tempFile.SeekToBegin();
  m_tempFile.Write(pBuf,14);

  m_tempFile.Close();


  return flg;

//it will capture wnd and save into a bmp file
//End of the code

}

//////////////////////////////////////////////////////////////////
Function Name :HANDLE DDBToDIB(CBitmap &bitmap,
                               DWORD dwCompression,
                               CPalette *pPal)
Parameters:
  CBitmap &bitmap : Compatible Device Dependent Bitmap
  DWORD dwCompression : Compression format for Bitmap must not be
  BI_BITFIELDS in this case.
  CPalette *pPal : Pointer to Palette. If this is NULL, the
  default system palette will be used.

//////////////////////////////////////////////////////////////////
HANDLE DDBToDIB(CBitmap &bitmap, DWORD dwCompression,
                CPalette *pPal)
{

  BITMAP              bm;
  BITMAPINFOHEADER    bi;
  LPBITMAPINFOHEADER  lpbi;
  DWORD               dwLen;
  HANDLE              hDIB;
  HANDLE              handle;
  HDC                 hDC;
  HPALETTE            hPal;


  ASSERT( bitmap.GetSafeHandle() );

  // The function has no arg for bitfields
  if( dwCompression == BI_BITFIELDS )
    return NULL;

  // If a palette has not been supplied, use default palette
  hPal = (HPALETTE) pPal->GetSafeHandle();
  if (hPal==NULL)
    hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);

  // Get bitmap information
  bitmap.GetObject(sizeof(bm),(LPSTR)&bm);

  // Initialize the bitmap infoheader
  bi.biSize          = sizeof(BITMAPINFOHEADER);
  bi.biWidth         = bm.bmWidth;
  bi.biHeight        = bm.bmHeight;
  bi.biPlanes        = 1;
  bi.biBitCount      = bm.bmPlanes * bm.bmBitsPixel;
    //bm.bmPlanes    * bm.bmBitsPixel;
  bi.biCompression   = dwCompression;
  bi.biSizeImage     = 0;
  bi.biXPelsPerMeter = 0;
  bi.biYPelsPerMeter = 0;
  bi.biClrUsed       = 0;
  bi.biClrImportant  = 0;

  // Compute the size of the infoheader and the color table
  int nColors = (1 << bi.biBitCount);
  if( nColors > 256 )
    nColors = 0;
  dwLen  = bi.biSize + nColors * sizeof(RGBQUAD);

  // We need a device context to get the DIB from
  hDC = ::GetDC(NULL);
  hPal = SelectPalette(hDC,hPal,FALSE);
  RealizePalette(hDC);

  // Allocate enough memory to hold bitmap infoheader and
  // color table
  hDIB = GlobalAlloc(GMEM_FIXED,dwLen);

  if (!hDIB){
    SelectPalette(hDC,hPal,FALSE);
    ::ReleaseDC(NULL,hDC);
    return NULL;
  }

  lpbi = (LPBITMAPINFOHEADER)hDIB;

  *lpbi = bi;

  // Call GetDIBits with a NULL lpBits param, so the device
  // driver will calculate the biSizeImage field
  GetDIBits(hDC, (HBITMAP)bitmap.GetSafeHandle(), 0L,
                 (DWORD)bi.biHeight,
                 (LPBYTE)NULL, (LPBITMAPINFO)lpbi,
                 (DWORD)DIB_RGB_COLORS);

  bi = *lpbi;

  // If the driver did not fill in the biSizeImage field, then
  // compute it
  // Each scan line of the image is aligned on a DWORD (32bit)
  // boundary
  if (bi.biSizeImage == 0){
    bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31)
                        & ~31) / 8) * bi.biHeight;

    // If a compression scheme is used, the result may in fact
    // be larger
    // Increase the size to account for this.
    if (dwCompression != BI_RGB)
      bi.biSizeImage = (bi.biSizeImage * 3) / 2;
  }

  // Realloc the buffer so that it can hold all the bits
  dwLen += bi.biSizeImage;
  if (handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE))
    hDIB = handle;
  else{
    GlobalFree(hDIB);

    // Reselect the original palette
    SelectPalette(hDC,hPal,FALSE);
    ::ReleaseDC(NULL,hDC);
    return NULL;
  }

  // Get the bitmap bits
  lpbi = (LPBITMAPINFOHEADER)hDIB;

  // FINALLY get the DIB
  BOOL bGotBits = GetDIBits( hDC, (HBITMAP)bitmap.GetSafeHandle(),
        0L,                        // Start scan line
        (DWORD)bi.biHeight,        // # of scan lines
        (LPBYTE)lpbi               // address for bitmap bits
        + (bi.biSize + nColors * sizeof(RGBQUAD)),
        (LPBITMAPINFO)lpbi,        // address of bitmapinfo
        (DWORD)DIB_RGB_COLORS);    // Use RGB for color table

  if( !bGotBits )
  {
    GlobalFree(hDIB);

    SelectPalette(hDC,hPal,FALSE);
    ::ReleaseDC(NULL,hDC);
    return NULL;
  }

  SelectPalette(hDC,hPal,FALSE);
  ::ReleaseDC(NULL,hDC);

  return hDIB;

//End of the function
}

Downloads

Download Project Source - 128 Kb