Capturing a Window Image Into a Bitmap File, Supporting MS PaintBrush and All -- (Like the Thumbnail View of Windows Explorer)
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 }

Comments
same program in sdk
Posted by pajaiswal on 09/05/2007 02:56ami need the sane program in SDK.can you help me.
ReplySmall bug
Posted by ryu on 11/08/2006 08:42pmThe code: hdr.bfOffBits=(DWORD) sizeof(hdr) + nColors * sizeof(RGBQUAD);
Replydoesn't contain the sizeof BITMAPINFO and therefore, the offset bits are wrong.
I think it should be:
hdr.bfOffBits=(DWORD) sizeof(hdr) + sizeof(BITMAPINFO) + ((nColors-1) * sizeof(RGBQUAD));
Btw, this is a great article. Thanks for sharing it with me :)
Cheers :)
Offset
Posted by wuseltum on 06/29/2006 11:15amThanks for your help - this was the first piece of code that worked for me ;) However I've got one problem: The bmp-file contains a certain displacement, a thin strip of the very right side of my window is displayed on the left in the bmp-file. Has anyone else encountered this problem? Thanks again!
-
-
ReplyRe: Offset
Posted by wuseltum on 10/24/2006 07:57amCool, that was it! Thanks a lot! :)
ReplyRe: Offset
Posted by zhouzhen1 on 10/08/2006 11:14amI got the same problem. And I find that the biSize is not included when calculating the header info. Actually it should be like this, hdr.bfOffBits=(DWORD) sizeof(hdr) + lpbi->biSize + nColors * sizeof(RGBQUAD);
ReplyGlobalFree after call to DDBToDIB
Posted by jazee on 07/28/2005 01:21pmThe hDIB should be freed after call to DDBToDIB() otherwise a memory leak occurs. In WndToBmpFile( ) Put GlobalFree( hDIB ); after m_file.Write(lpbi,GlobalSize(hDIB)); m_file.Close();
ReplyWriting failed in 16 BPP
Posted by yulaw_hieu on 05/05/2005 12:02amI try in 16 BPP mode and the image can't display
-
ReplyFix for 16 bit image
Posted by jazee on 07/28/2005 01:25pmThe code has a bug in the offset of pBuf Should be 12 not 13. The fix below will make the output match Photoshop 16 bit BMP. (The only difference is file length is 2 bytes short.) Photoshop and Paint open the output. I used: #define BMPSIZE 48 CFile m_tempFile; //CBinFile is derived from CFile BYTE dummy=0; BYTE pBuf[BMPSIZE]; BOOL fres=m_tempFile.Open(strFile,CFile::modeReadWrite |CFile::typeBinary); if(fres!=0) { UINT tt=m_tempFile.Read(pBuf,BMPSIZE); pBuf[12]=dummy;/ /will replace from 04 to 00 pBuf[2]=(BYTE)0x38; pBuf[10]=(BYTE)0x36; pBuf[34]=(BYTE)0x02; pBuf[38]=(BYTE)0x12; pBuf[39]=(BYTE)0x0B; pBuf[42]=(BYTE)0x12; pBuf[43]=(BYTE)0x0B; m_tempFile.SeekToBegin(); m_tempFile.Write(pBuf,BMPSIZE); }ReplyCode is crashing....
Posted by SanjeevKumar on 05/04/2004 11:08amCode is crashing....
Posted by SanjeevKumar on 05/04/2004 11:07am