C++ Windows function to save contents of window to bitmap - c++

I am attempting to write a C++ program that involves processing data captured from a window. I am intentionally trying to write this myself using windows for the experience, so please do not recommend libraries that already exist to make this task easier. Anyway, for the sake of testing, I have written a function that should accept a hwnd handle and an hdc device context, and use that information to create a bitmap image file of the client area of the window reference by hwnd. The code I have written is heavily based on this sample code from MSDN. I am just trying to capture the data for now, not display it to the screen or modify it in any way. I have also heavily commented the code to give an idea of what exactly it is I think I am doing with each line. Please let me know if I have any fundamental misunderstandings here.
void screenshot(HWND hwnd, HDC hdc){
HDC clientarea = hdc; // The device context for the client area of the window
HDC memory = NULL; // client context for storing the bitmap in memory
RECT clientdim; // Defines memeory location for dimensions of client window
GetClientRect(hwnd, &clientdim); // Stores the client dimesnion info at clientdim location
BITMAP img; // Points to the where the bitmap handle will write it's data once it contains the bitmap data for the client window
HBITMAP clienthandle = CreateCompatibleBitmap(clientarea, clientdim.right - clientdim.left, clientdim.bottom - clientdim.top); // Bitmap handle, actual data of the rectangle defined at clientdim
SelectObject(memory, clienthandle); // Selecting the bitmap handle into the memory context allows us to bitblit the bitmap data to the bitmap handle
BitBlt(memory, 0, 0, clientdim.right - clientdim.left, clientdim.bottom - clientdim.top, clientarea, 0, 0, SRCCOPY);
// The bitmap handle now contains the data defined by the rectangle coordinates, which the dimensions of the hwnd client area
// The source and desitnation nXdest, nYdest, etc. fields are with respect the the window, not the screen, so they are 0
// Gets the bitmap data from the handle, and stores it to a memory location as an actual bitmap file
GetObject(clienthandle, sizeof(BITMAP), &img);
// Info head for the bitmap. Information about the dimesions and colors
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER); // Sets the size of the header literally to the size of an info header.
bmih.biWidth = img.bmWidth; // Sets the width of the bitmap to the width of the bitmap in memory
bmih.biHeight = img.bmHeight; // Sets the height of the bitmap to the height of the bitmap in memory
bmih.biPlanes = 1; // number of planes must be 1 for bitmaps to save
bmih.biBitCount = 32; // 32-bit color. Means RGB dat is stored per pixel.
bmih.biCompression = BI_RGB; // Tells it to use the RGB compression, as defined by the 32bit pixels
bmih.biSizeImage = 0; // Size of compression buffer. RGB is uncompressed, so is 0 for now
bmih.biXPelsPerMeter = 0; // Defines the number of pixels per meter in the x axis. Only used by some programs to select images, we don't care.
bmih.biYPelsPerMeter = 0; // Same as above, but fore y axis.
bmih.biClrUsed = 0; // the number of color indexes the bitmat uses. 0 means it uses all of them
bmih.biClrImportant = 0; // The number of color indexes actually required to display a pixel. 0 means we need all of them.
DWORD bitmapsize = ((img.bmWidth * bmih.biBitCount + 31) / 32) * 4 * img.bmHeight; // Total size of the bitmap. Not sure why this works, will look into it
HANDLE hDIB = GlobalAlloc(GHND, bitmapsize); // A handle to heap memory of size bitmapsize where our bitmap is processed. GHND = Initializes memory to zero an
//allows location to translate to pointer with globallock, called such because it locks the memory in place within the heap
// hDIB because it is a Device Independent Bitmap
char *lpbitmap = (char *)GlobalLock(hDIB); // Stores the contents of the char at the memory locattion defined by GlobalLock(). Basically, this is the bitmap in the heap.
GetDIBits(clientarea, clienthandle, 0, (UINT)img.bmHeight, lpbitmap, (BITMAPINFO *)&bmih, DIB_RGB_COLORS); // Gets the bits from the bitmap and copies them to the buffer found at lpbitmap
HANDLE hFile = CreateFile(L"capture.bmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // Creates file with various attributes. Check the documentation on MSDN.
DWORD dibSize = bitmapsize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //Size of the dib is the size of the actual bitmap plus the size of its headers
BITMAPFILEHEADER bmfh; // Fileheader for the bitmap file
bmfh.bfType = 0x4D42; // Defines file as bitmap. 0x4D42 is the code for this
bmfh.bfSize = dibSize; // The total size is everything in the DIB
bmfh.bfReserved1 = 0; // These reserved values must be zero
bmfh.bfReserved2 = 0;
bmfh.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); // Offset to where the image begins. It begins after the header data
// File headers are written first, then the actual bitmap data.
DWORD byteswritten = 0; //Used in writefile commands to accept number of bytes written. I'd have to use an overlapped structure otherwise
WriteFile(hFile, (LPSTR)&bmfh, sizeof(BITMAPFILEHEADER), &byteswritten, NULL); // Look at documentation for remind on what the hell is happening here
WriteFile(hFile, (LPSTR)&bmih, sizeof(BITMAPINFOHEADER), &byteswritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, bitmapsize, &byteswritten, NULL);
// Unlcok DIB from the heap, then free it's memory back up
GlobalUnlock(hDIB);
GlobalFree(hDIB);
// Close the file handle
CloseHandle(hFile);
// Delete all of the objects and release the device contexts
DeleteObject(memory);
ReleaseDC(hwnd, clientarea);
// Donezo!!
HDC hdc is the device context of the window indicated by hwnd. I pass both of these to the function instead of just using GetDC(hwnd) in the function itself because I already have the device context from elsewhere in the code. I assume this is more efficient.
Any guidance at all would be very helpful. Thank you.
EDIT: It was pointed out that I forgot the actual question in the initial post... The problem I am having is this code is saving a bitmap that is just a black box. It does not actually capture the contents of the window.

Here is a version that works for me.
void screenshot(HWND hWnd,wchar_t* fileName){
HBITMAP hbmScreen{NULL};
BITMAP bmpScreen;
DWORD dwBytesWritten = 0;
DWORD dwSizeofDIB = 0;
HANDLE hFile = NULL;
char* lpbitmap = NULL;
HANDLE hDIB = NULL;
DWORD dwBmpSize = 0;
// Retrieve the handle to a display device context for the client
// area of the window.
const HDC hdcScreen = GetDC(hWnd);
// Create a compatible DC, which is used in a BitBlt from the window DC.
const HDC hdcMemDC = CreateCompatibleDC(hdcScreen);
if (!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
goto done;
}
// Get the client area for size calculation.
RECT rcClient;
GetClientRect(hWnd, &rcClient);
// This is the best stretch mode.
SetStretchBltMode(hdcScreen, HALFTONE);
// Create a compatible bitmap from the Window DC.
hbmScreen = CreateCompatibleBitmap(hdcScreen, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
if (!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcScreen,
0, 0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
// Get the BITMAP from the HBITMAP.
GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
// Gets the "bits" from the bitmap, and copies them into a buffer
// that's pointed to by lpbitmap.
GetDIBits(hdcScreen, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
hFile = CreateFile(fileName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size.
dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
// Size of the file.
bmfHeader.bfSize = dwSizeofDIB;
// bfType must always be BM for Bitmaps.
bmfHeader.bfType = 0x4D42; // BM.
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
// Unlock and Free the DIB from the heap.
GlobalUnlock(hDIB);
GlobalFree(hDIB);
// Close the handle for the file that was created.
CloseHandle(hFile);
// Clean up.
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(hWnd, hdcScreen);
}

Related

Crop an HBITMAP with C++ on Windows

I have an HBITMAP holding the screenshot of a window. Now I want to crop a certain region/rectangle out of it and return it as a new HBITMAP.
The following code however only squeezes the image into the correct new rectangle size but does not crop it:
HBITMAP crop_image(const RECT rectangle, const HBITMAP source_image)
{
const auto h_clone = static_cast<HBITMAP>(CopyImage(source_image, IMAGE_BITMAP, rectangle.right - rectangle.left,
rectangle.bottom - rectangle.top, LR_CREATEDIBSECTION));
const auto hdc_mem = CreateCompatibleDC(nullptr);
const auto hdc_mem2 = CreateCompatibleDC(nullptr);
const auto h_old_bmp = static_cast<HBITMAP>(SelectObject(hdc_mem, source_image));
const auto h_old_bmp2 = static_cast<HBITMAP>(SelectObject(hdc_mem2, h_clone));
BitBlt(hdc_mem2, 0, 0, rectangle.right - rectangle.left, rectangle.bottom - rectangle.top,
hdc_mem, rectangle.left, rectangle.top, SRCCOPY);
SelectObject(hdc_mem, h_old_bmp);
SelectObject(hdc_mem2, h_old_bmp2);
DeleteDC(hdc_mem);
DeleteDC(hdc_mem2);
return h_clone;
}
How can I fix my code to crop the image as desired?
I'm not sure if this going to work or not due to I haven't been working on Win32 for a long time, so let's try.
Here is the idea:
Create a new memory DC.
Create a new bitmap instead of cloning from the source.
BitBlt the target rectangle of the source bitmap into the one that just created.
Create a new DC and bitmap
Use CreateCompatibleDC as you did to create a memory DC. Then create a new bitmap with CreateCompatibleBitmap. The width and height of the bitmap will be the size of the cropped bitmap. Then select it into the DC with SelectObject.
Copy the target rectangle from the source
Now you need to create a new DC for the source bitmap with CreateCompatibleDC and select the source bitmap into it with SelectObject. Then use BitBlt to copy the rectangle of the source bitmap into the bitmap that you just created. The destination x and y will be zero because we want to start drawing from the upper-left. The source x and y is where you want to start copy from the source bitmap.
Your code can be reduced to this:
HBITMAP crop_image(const RECT rectangle, const HBITMAP source_image)
{
const auto h_clone = static_cast<HBITMAP>(CopyImage(source_image, IMAGE_BITMAP, rectangle.right - rectangle.left,
rectangle.bottom - rectangle.top, LR_CREATEDIBSECTION));
return h_clone;
}
The deleted code doesn't do anything useful. After I test, I can get the cropped area, not the reduced screenshot. Because you didn't provide the complete code, I couldn't get more conclusions.
For the process of clipping screenshots, we can easily modify it according to the official example.
Capturing an Image
The APIs used are described in detail in the example.
You only need to change some of the code, such as changing the cropped area, and the example also shows how to save the cropped area as a file.
int CaptureAnImage(HWND hWnd)
{
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = 800;
rc.bottom = 600;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if (!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
}
// Get the client area for size calculation
RECT rcClient;
GetClientRect(hWnd, &rcClient);
//This is the best stretch mode
SetStretchBltMode(hdcWindow, HALFTONE);
//The source DC is the entire screen and the destination DC is the current window (HWND)
if (!StretchBlt(hdcWindow,
0, 0,
rcClient.right, rcClient.bottom,
hdcScreen,
0, 0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SRCCOPY))
{
MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);
}
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rc.right - rc.left, rc.bottom - rc.top);
if (!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
rc.left, rc.top,
rc.right - rc.left, rc.bottom - rc.top,
hdcWindow,
0, 0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
}
// HBITMAP bmpnew = crop_image(rc, hbmScreen);
// Get the BITMAP from the HBITMAP
GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(L"captureqwsx.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Close the handle for the file that was created
CloseHandle(hFile);
//Clean up
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hWnd, hdcWindow);
return 0;
}
It will get a cropped screenshot(800x600) file of the current screen.

win32 bitmap erroneously converted to monochrome

I'm trying to get the bits of a window icon, in order to send them through a socket. To test if I'm hable to get the BITMAP corresponding to the HICON, I save it on a file.
The problem is that, opening the file where I've saved the bitmap, it appear monochrome (black and white, not grey scale).
Here is the BITMAPINFO struct:
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = bitmap.bmWidth;
info.biHeight = bitmap.bmHeight;
info.biPlanes = 1;
info.biBitCount = 32;
info.biCompression = BI_RGB;
info.biSizeImage = 0;
info.biXPelsPerMeter = 0;
info.biYPelsPerMeter = 0;
info.biClrUsed = 0;
info.biClrImportant = 0;
And I compute the size of the bitmap as:
DWORD dwBmpSize2 = ((bitmap.bmWidth * bi.biBitCount + 31) / 32) * 4 * bitmap.bmHeight;
Then:
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
//Prendo i bit dell'icona
HDC hdc = GetDC(hwnd);
GetDIBits(hdc, hbit, 0, (UINT)bitmap.bmHeight, lpbitmap, (BITMAPINFO*)&bi,DIB_RGB_COLORS);
wcout « "lpbitmap buffer: "« lpbitmap « endl;
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(L"captureqwsx.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

Win32 Create Bitmap from Device Context To File and/or BLOB

I would like to get the context of my window into a bitmap. I use the window to draw basic lines with touch. The problem that I'm having is my bitmap is black. Which it is probably due to the fact that I'm not grabbing the device context properly or doing something else wrong.
The functions CreateBitmapInfoStruct and CreateBMPFile are from this MSDN example.
Also note that g_hWnd is a global variable that has the handle of the window for which I want to save the picture.
My end goal is to be able to save the bitmap into a mysql field (BLOB) that I have. This is what my original problem was. Anyway, I started by first trying to create a BMP to file.
I have searched here and in other places. The best solution I found was recommended here following this MSDN example. However, it is not working.
Any help for this specific problem and/or in writing to the bitmap into a blob into mysql table, will be greatly appreciated.
Here is my code:
HDC hDC = GetDC(g_hWnd);
LPRECT rect = (LPRECT)malloc(sizeof(RECT));
GetWindowRect(g_hWnd,rect);
int h = rect->right - rect->left;
int w = rect->bottom - rect->top;
LPRECT rect = (LPRECT)malloc(sizeof(RECT));
GetWindowRect(g_hWnd,rect);
HBITMAP hBmp = CreateCompatibleBitmap(hDC,w,h);
PBITMAPINFO pbmi;
pbmi = CreateBitmapInfoStruct(g_hWnd,hBmp);
CreateBMPFile(g_hWnd, TEXT("c:\\TEMPO\\TestG2.bmp"), pbmi,
hBmp, hDC) ;
ReleaseDC(g_hWnd,hDC);
DeleteObject(hBmp);
DeleteObject(pbmi);
if (rect != nullptr)
free(rect);
EDIT:
The actual answer for capturing the screen (getDC) is to modify this sample in MSDN.
I have modified the sample here (remove the stretch) Note that still uses the goto, which I will remove.
A note about the GOTO... WHile I don't use it, I don't find it to be a problem either. I think too much has been made about the GOTO statement... like if in assembly, we wouldn't do goto (JUMPS)
Here is the code:
void saveBitmap()
{
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(g_hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC)
{
goto done;
}
RECT rcClient;
GetClientRect(g_hWnd, &rcClient);
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left,
rcClient.bottom-rcClient.top);
if(!hbmScreen)
{
goto done;
}
SelectObject(hdcMemDC,hbmScreen);
if(!BitBlt(hdcMemDC,
0,0,
rcClient.right-rcClient.left, rcClient.bottom-rcClient.top,
hdcWindow,
0,0,
SRCCOPY))
{
goto done;
}
GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 *
bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(L"c:\\tempo\\captureqwsx.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) +
(DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Close the handle for the file that was created
CloseHandle(hFile);
//Clean up
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL,hdcScreen);
ReleaseDC(g_hWnd,hdcWindow);
}
You can use GetDIBits().
This function copies the image pixels data into your own allocated memory.
See GetDIBits and loop through pixels using X, Y for some more details and explanation.

Why is GetClientRect including the window border and title bar?

I'm writing some code that will take a screenshot of another application, given its' window handle, in C++. The method I am using is to use BitBlt. My application is successfully taking the screenshot and I have a function that will save that image data out to a bmp file.
The screenshot contains the window's chrome though. That is, the border and the title bar. Based on my understanding, GetClientRect is supposed to exclude the window's border and title bar. I understand that GetWindowRect returns the coordinates within the user's desktop, and GetClientRect returns the coordinates relative to the application itself.
I notice in my screenshots that the title bar and left border are visible, but the right border and bottom of the application are cut off. So, I'm thinking that if I want to exclude the title and borders then I need to be doing some sort of combination of GetWindowRect and GetClientRect, and using information about the window itself to offset the GetClientRect dimensions by whatever the height of the window's title bar is, for example.
Does this sound accurate, or is my code below doing something wrong?
#include <Windows.h>
#include "ScreenshotManager.h"
namespace Managers {
ScreenshotManager::ScreenshotManager(HWND gameHandle) {
// get a device context for the window
m_gameContext = GetWindowDC(gameHandle);
// create a compatible device context for bitblt
m_bitmapContext = CreateCompatibleDC(m_gameContext);
// get window client area dimensions
GetClientRect(gameHandle, &m_gameClientArea);
}
bool ScreenshotManager::TakeScreenshot() {
// create a compatible bitmap for the game screenshots
m_bitmap = CreateCompatibleBitmap(m_gameContext, m_gameClientArea.right, m_gameClientArea.bottom);
// select the bitmap into the compatible device context
SelectObject(m_bitmapContext, m_bitmap);
// perform bit block transfer
if (BitBlt(m_bitmapContext, 0, 0, m_gameClientArea.right, m_gameClientArea.bottom, m_gameContext, 0, 0, SRCCOPY) == false)
return false;
// get information about the taken screenshot
GetObject(m_bitmap, sizeof(BITMAP), &m_bitmapInformation);
return true;
}
void ScreenshotManager::SaveScreenshot(LPCWSTR outputPath) {
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = m_bitmapInformation.bmWidth;
bi.biHeight = m_bitmapInformation.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((m_bitmapInformation.bmWidth * bi.biBitCount + 31) / 32) * 4 * m_bitmapInformation.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Gets the "bits" from the bitmap and copies them into a buffer which is pointed to by lpbitmap.
GetDIBits(m_gameContext, m_bitmap, 0, (UINT)m_bitmapInformation.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(outputPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Close the handle for the file that was created
CloseHandle(hFile);
}
}
GetClientRect() does not include the border and title bar. All it does is tell you the dimensions of the client area.
BitBlt() copies a rectangular region of pixels from one device context to another. In this example the source DC is a window DC, so the origin coordinates are relative to that window.
What your code is doing is copying a client-sized rectangle from the window’s origin. (That’s why the right and bottom edges go missing.)
You might be interested in AdjustWindowRectEx() to help identify the coordinates of the region you want to copy.

How to update a user created Bitmap in the Windows API

In my code I quickly generate images on the fly, and I want to display them as quickly as possible. So the first time I create my image, I create a new BITMAP, but instead of deleting the old one and creating a new one for every subsequent image, I just want to copy my data back into the existing one. Here is my code to do both the initial creation and the updating. The creation works just fine, but the updating one doesn't work.
BITMAPINFO bi;
HBITMAP Frame::CreateBitmap(HWND hwnd, int tol1, int tol2, bool useWhite, bool useBackground)
{
ZeroMemory(&bi.bmiHeader, sizeof(BITMAPINFOHEADER));
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biCompression = BI_RGB;
ZeroMemory(bi.bmiColors, sizeof(RGBQUAD));
// Allocate memory for bitmap bits
int size = height * width;
Pixel* newPixels = new Pixel[size];
// Recompute the output
//memcpy(newPixels, pixels, size*3);
ComputeOutput(newPixels, tol1, tol2, useWhite, useBackground);
HBITMAP bitmap = CreateDIBitmap(GetDC(hwnd), &bi.bmiHeader, CBM_INIT, newPixels, &bi, DIB_RGB_COLORS);
delete newPixels;
return bitmap;
}
and
void Frame::UpdateBitmap(HWND hwnd, HBITMAP bitmap, int tol1, int tol2, bool useWhite, bool useBackground)
{
ZeroMemory(&bi.bmiHeader, sizeof(BITMAPINFOHEADER));
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
HDC hdc = GetDC(hwnd);
if(!GetDIBits(hdc, bitmap, 0, bi.bmiHeader.biHeight, NULL, &bi, DIB_RGB_COLORS))
MessageBox(NULL, "Can't get base image info!", "Error!", MB_ICONEXCLAMATION | MB_OK);
// Allocate memory for bitmap bits
int size = height * width;
Pixel* newPixels = new Pixel[size];
// Recompute the output
//memcpy(newPixels, pixels, size*3);
ComputeOutput(newPixels, tol1, tol2, useWhite, useBackground);
// Push back to windows
if(!SetDIBits(hdc, bitmap, 0, bi.bmiHeader.biHeight, newPixels, &bi, DIB_RGB_COLORS))
MessageBox(NULL, "Can't set pixel data!", "Error!", MB_ICONEXCLAMATION | MB_OK);
delete newPixels;
}
where the Pixel struct is just this:
struct Pixel { unsigned char b, g, r; };
Why does my update function not work. I always get the MessageBox for "Can't set pixel data!" I used code similar to this when I was loading in the original bitmap from file, then editing the data, but now when I manually create it, it doesn't work.
to write to a bitmap you have to Select it into a DeviceContext:
hdcMemDC = CreateCompatibleDC(hdc);
... // adjust hdcMem to right sizes (see MSDN : Capturing an Image)
SelectObject(hdcMemDC,bitmap);
... // your Bitmap drawing
BitBlt( hdcMemDC, ... ) // to copy to hdc.
ReleaseDC(hWnd, hdcMemDC);
Also CreateDIBitmap( GetDC(hwnd),... ) needs a ReleaseDC(hWnd ,??? );
You must assign GetDC(...) to a variable so you can release it.
Please, create your bitmap with CreateDIBSection.
This creates a HBITMAP to a bitmap and returns a pointer to a usermode accessible buffer. In the format you specify.