C++ take snapshot bitmap returns black image - c++

basically what i'm trying to achieve is a continuous flow of taking snapshots of specific part of a window. Up to now I've managed to take the screenshot and parse it pixelwise. However, all screenshots are black. Please help this newbie. No errors returned, FindWindow works and returns the correct window(to appwnd). I've been debugging this for the past 3 days and i'm out of ideas. Also i need to mention i am releasing the memory after using everything, to not clamp up my ram.I'm running Windows 10 last patch.
snippet of Code:
BOOL myfunction(appwnd){
RECT rcWindow;
GetWindowRect(appwnd, &rcWindow);
BITMAP bm;
HBITMAP hbmap;
HBITMAP hbmapold;
BITMAPINFO bmi;
HDC hdcShot;
//a while starts here(not the issue)
RECT rc;//get window rectangle
GetWindowRect(appwnd, &rc);
//creating bitmaps
HDC hdc = GetDC(0);
hdcShot = CreateCompatibleDC(hdc);
hbmap = CreateCompatibleBitmap(hdc, rc.right - rc.left, rc.bottom - rc.top);
//
if (!BitBlt(hdcShot, 0, 0, rc.right - rc.left, rc.bottom - rc.top, GetDC(0), rc.left, rc.right, SRCCOPY | CAPTUREBLT))
{
printf("bitblt failed");
system("pause");
}
SelectObject(hdcShot, hbmap);
if (!GetObject(hbmap, sizeof(BITMAP), (LPSTR)&bm))
{
printf("error at getobject");
system("pause");
return false;
}
int bitsperpixel = bm.bmBitsPixel;
if (bitsperpixel != 32 || bm.bmPlanes != 1)
{
printf("error at bitsperpixel/bm planes");
system("pause");
return false;
}
SetupBitmapInfo(bmi, bm.bmWidth, bm.bmHeight, bitsperpixel);
HDC hdcShotNew = CreateCompatibleDC(hdcShot);
HBITMAP hbmapNew = CreateCompatibleBitmap(hdcShot, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
HBITMAP OldBmp = (HBITMAP)SelectObject(hdcShotNew, hbmapNew);
int width = rcWindow.right - rcWindow.left;
int height = rcWindow.bottom - rcWindow.top;
printf("\nwidth:%i", width);
printf("\nheight:%i", height);
//copy the screen using bitblt
BitBlt(hdcShotNew, 0, 0, width, height , hdcShot,0,0,SRCCOPY|CAPTUREBLT);
pPixels = new RGBQUAD[bm.bmWidth * bm.bmHeight];
if (!pPixels)
{
printf("ppixels failed");
return false;
}
SelectObject(hdcShotNew, OldBmp);
if (GetDIBits(hdcShotNew, hbmapNew, 0, bm.bmHeight, pPixels, &bmi, DIB_RGB_COLORS)==0)
{
ReleaseDC(appwnd, hdcShot);
printf("getdibits failed");
delete[] pPixels;
return false;
}
}
Snippet of printing pixels :
printf("\n%i,%i,%i", (int)pPixels[p].rgbRed, (int)pPixels[p].rgbGreen, (int)pPixels[p].rgbBlue);
all pixels are 0, always.
Snippet of SetupBitmapInfo:
void SetupBitmapInfo(BITMAPINFO& bmi, int bWidth, int bHeight, int bitsperpixel)
{
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = bWidth;
bmi.bmiHeader.biHeight = bHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = bitsperpixel;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
}

Related

Capturing a specific window using C++ returns old data

I'm currently working on a project that requires to take a screenshot a specific window.
That's what I got so far:
Main function
int main() {
LPCSTR windowname = "Calculator";
HWND handle = FindWindowA(NULL, windowname);
while (!handle) {
std::cout << "Process not found..." << std::endl;
handle = FindWindowA(NULL, windowname);
Sleep(100);
}
Mat img = captureScreenMat(handle);
resetMat();
imwrite("test2.jpg", img);
showInMovedWindow("IMG", img);
waitKey(0);
destroyAllWindows();
return 0;
}
winCapture
Mat src;
void showInMovedWindow(string winname, Mat img) {
namedWindow(winname);
moveWindow(winname, 40, 30);
imshow(winname, img);
}
BITMAPINFOHEADER createBitmapHeader(int width, int height)
{
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height;
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;
return bi;
}
int getHeight(HWND hwnd) {
RECT rect;
GetWindowRect(hwnd, &rect);
int height = rect.bottom - rect.top;
return height;
}
int getWidth(HWND hwnd) {
RECT rect;
GetWindowRect(hwnd, &rect);
int width = rect.right - rect.left;
return width;
}
Mat captureScreenMat(HWND hwnd)
{
HDC hwindowDC = GetDC(hwnd);
HDC hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
int screenx = GetSystemMetrics(SM_XVIRTUALSCREEN);
int screeny = GetSystemMetrics(SM_YVIRTUALSCREEN);
int width = getWidth(hwnd);
int height = getHeight(hwnd);
src.create(height, width, CV_8UC4);
HBITMAP hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
BITMAPINFOHEADER bi = createBitmapHeader(width, height);
//DEBUG
cout << hbwindow << endl;
cout << hwindowCompatibleDC << endl;
cout << hwindowDC << endl;
SelectObject(hwindowCompatibleDC, hbwindow);
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, screenx, screeny, width, height, SRCCOPY);
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
DeleteObject(hbwindow);
DeleteDC(hwindowCompatibleDC);
DeleteDC(hwindowDC);
ReleaseDC(NULL, hwindowDC);
return src;
}
void resetMat() {
src.release();
}
Most windows work really well with this approach, but there are some windows that are working the first time, I try to take a img of them, but every time I try to take another screenshot of the same process, it just gives me the first screenshot I took of it. It only works again after a restart of the process and even then It just works again for one screenshot and all after are the same.
I thought it would be some kind of memory leak, but I'm deleting all the objects and releasing the handle.
I think that something is wrong with the handle, but I couldn't figure out what.
I'm not familiar with working with the windowsAPI and hope someone knowns more than me.
Fixed: Process blocked creating handle.
When you call SelectObject you must save the previous-selected handle (available from the return value) and you MUST select it back before deleting or releasing the device context.
Right now you are breaking a bunch of rules.
Deleting a bitmap which is selected into a device context.
Deleting a DC gotten from GetDC.
Calling both DeleteDC and ReleaseDC on the same handle.
Passing NULL as the first parameter of ReleaseDC, which should be the same HWND passed to GetDC.
Deleting a DC without selecting the original bitmap back into it.
These bugs royally mess up the window DC. If it was a transient DC, the system probably cleans up your mess immediately. But if the window class has the CS_OWNDC or CS_CLASSDC flags, you do permanent damage to the window. That's why your method appears to work with some windows and not others.

ATL: OnDrawThumbnail hDrawDC seems to be monochrome in IThumbnailProvider

I'm working on a C++ ATL COM thumbnail/preview/search project, but its bitmap displaying code behavior is monochrome during the Thumbnail process instead of the colored. The Preview process is colored as expected, using the same function.
I used the ATL Wizard to create the IThumbnailProvider and his friends. My small changes are: I replaced the color from black to pink in the document::OnDrawThumbnail and I wrote the document::OnDrawThumbnail into CPreviewCtrl::DoPaint. I've read the "new DC always monochrome" thing in the MS spec but I could not get colored DC even if a changed the original ATL code OnDrawThumbnail(GetDC(NULL), &rcBounds);. The CreateCompatibleDC(NULL) and CreateCompatibleDC(hDrawDC) were dead-end too.
document.cpp (changed)
// Almost the default sample code, but hDrawBrush is changed to pink
void document::OnDrawThumbnail(HDC hDrawDC, LPRECT lprcBounds)
{
HBRUSH hDrawBrush = CreateSolidBrush(RGB(255, 0, 255)); // pink
FillRect(hDrawDC, lprcBounds, hDrawBrush);
HFONT hStockFont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
LOGFONT lf;
GetObject(hStockFont, sizeof(LOGFONT), &lf);
lf.lfHeight = 34;
HFONT hDrawFont = CreateFontIndirect(&lf);
HFONT hOldFont = (HFONT) SelectObject(hDrawDC, hDrawFont);
CString strText = _T("TODO: implement thumbnail drawing here");
DrawText(hDrawDC, strText, strText.GetLength(), lprcBounds, DT_CENTER | DT_WORDBREAK);
SelectObject(hDrawDC, hDrawFont);
SelectObject(hDrawDC, hOldFont);
DeleteObject(hDrawBrush);
DeleteObject(hDrawFont);
}
PreviewHandler.h (changed, it is called by the Preview)
// CPreviewCtrl implementation
class CPreviewCtrl : public CAtlPreviewCtrlImpl
{
protected:
virtual void DoPaint(HDC hdc)
{
// you can obtain a pointer to IDocument as follows
// CMyDoc* pDoc = (CMyDoc*)m_pDocument;
/*
CString strData = _T("Draw Rich Preview content here.");
TextOut(hdc, 10, 20, strData, strData.GetLength());
*/
RECT rc{};
rc.right = 290;
rc.bottom = 290;
dynamic_cast<document*>(m_pDocument)->OnDrawThumbnail(hdc, &rc);
}
};
atlhandlerimpl.h (unchanged, from VS SDK \atlmfc\include\ which is called be the thumbnail provider)
ATLPREFAST_SUPPRESS(6101)
_Success_(return != FALSE) BOOL GetThumbnail(
_In_ UINT cx,
_Out_ HBITMAP* phbmp,
_Out_ WTS_ALPHATYPE* /* pdwAlpha */)
{
HDC hdc = ::GetDC(NULL);
RECT rcBounds;
SetRect(&rcBounds, 0, 0, cx, cx);
HDC hDrawDC = CreateCompatibleDC(hdc);
if (hDrawDC == NULL)
{
ReleaseDC(NULL, hdc);
return FALSE;
}
HBITMAP hBmp = CreateCompatibleBitmap(hDrawDC, cx, cx);
if (hBmp == NULL)
{
ReleaseDC(NULL, hdc);
DeleteDC(hDrawDC);
return FALSE;
}
HBITMAP hOldBitmap = (HBITMAP) SelectObject(hDrawDC, hBmp);
// Here you need to draw the document's data
OnDrawThumbnail(hDrawDC, &rcBounds);
SelectObject(hDrawDC, hOldBitmap);
DeleteDC(hDrawDC);
ReleaseDC(NULL, hdc);
*phbmp = hBmp;
return TRUE;
}
ATLPREFAST_UNSUPPRESS()
Sample thumbnail in the File Explorer
Github helped me. It is definitely an ATL SDK bug.
BUG report on the VS developer community
Solution on the www.patthoyts.tk
And the github repo which helped me: abhimanyusirohi/ThumbFish
In the atlhandlerimpl.h provided GetThumbnail must be override:
BOOL document::GetThumbnail(
_In_ UINT cx,
_Out_ HBITMAP * phbmp,
_Out_ WTS_ALPHATYPE* /* pdwAlpha */)
{
BOOL br = FALSE;
HDC hdc = ::GetDC(NULL);
HDC hDrawDC = CreateCompatibleDC(hdc);
if (hDrawDC != NULL)
{
void* bits = 0;
RECT rcBounds;
SetRect(&rcBounds, 0, 0, cx, cx);
BITMAPINFO bi = { 0 };
bi.bmiHeader.biWidth = cx;
bi.bmiHeader.biHeight = cx;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biSizeImage = 0;
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;
HBITMAP hBmp = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
if (hBmp != NULL)
{
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hDrawDC, hBmp);
OnDrawThumbnail(hDrawDC, &rcBounds);
SelectObject(hDrawDC, hOldBitmap);
*phbmp = hBmp;
br = TRUE;
}
DeleteDC(hDrawDC);
}
ReleaseDC(NULL, hdc);
return br;
}

Capture pixel data from only a specified window

I want the code below to take a screenshot of a specified window only, becuse this way BitBlt is faster.
The code below takes a screenshot of a window, specified by the name of window, loads the pixel data in a buffer and then re-draws the picture on your screen just to prove the copying worked.
I'd like to take screenshots of browser windows like Google Chrome, but it doesn't seem to work. It draws a black rectangle on my screen with the correct dimensions of the window.
Seems to be working all the time with the window of Minecraft.
Also worked with the Tor Browser.
I noticed that after querying window info, minecraft's window had no child-windows, but when it didn't work with the others, all of them had multiple child-windows.
#include<iostream>
#include<Windows.h>
#include<vector>
using namespace std;
int main() {
HWND wnd = FindWindow(NULL, "Minecraft 1.15.1");//Name of window to be screenshoted
if (!wnd) {
std::cout << "e1\n";
std::cin.get();
return 0;
}
WINDOWINFO wi = { 0 };
wi.cbSize = sizeof(WINDOWINFO);
GetWindowInfo(wnd, &wi);
RECT rect;
GetWindowRect(wnd, &rect);
int width = wi.rcClient.right - wi.rcClient.left;
int height = wi.rcClient.bottom - wi.rcClient.top;
cout << width << height;
/////Fetch window info^^^^^^^^
BYTE* ScreenData = new BYTE[4 * width*height];
// copy screen to bitmap
HDC hScreen = GetWindowDC(wnd);
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, width, height);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BitBlt(hDC, 0, 0, width, height, hScreen, 0, 0, SRCCOPY);
SelectObject(hDC, old_obj);
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
std::cout << GetDIBits(hDC, hBitmap, 0, 0, NULL, &bmi, DIB_RGB_COLORS)<<endl;
//std::cout << bmi.bmiHeader.biHeight<< " "<< bmi.bmiHeader.biWidth<<endl;
BYTE*buffer = new BYTE[4* bmi.bmiHeader.biHeight*bmi.bmiHeader.biWidth];
bmi.bmiHeader.biHeight *= -1;
std::cout<<GetDIBits(hDC, hBitmap, 0, bmi.bmiHeader.biHeight, buffer, &bmi, DIB_RGB_COLORS)<<endl;//DIB_PAL_COLORS
/////Load window pixels into a buffer^^^^^^^^
POINT p;
int r = 0;
int g = 0;
int b = 0;
int x = 0;
int y = 0;
HDC sc = GetDC(NULL);
COLORREF color;
while (true) {
if ((y*-1) == bmi.bmiHeader.biHeight+1 && x == bmi.bmiHeader.biWidth-1) { break; }
r = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x)+2];
g = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x)+1];
b = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x) ];
color = RGB(r, g, b);
SetPixel(sc, x, y, color);
x++;
if (x == bmi.bmiHeader.biWidth) {
y++;
x = 0;
}
}
/////Prove that the copying was successful and buffer is full^^^^^^^^
Sleep(5000);
std::cout << "fin\n";
std::cin.get();
return 0;
}
I think the problem is the order you're doing things.
Before you put the bitmap on the clipboard, you should select it out of your memory device context.
Once you put the bitmaps on the clipboard, you no longer own it, so you shouldn't try to delete it.
The best way to do that is probably to move your // clean up section before the // save bitmap to clipboard section and to eliminate your DelectObject(hBitmap) statement. So the tail end of your code should probably be:
BitBlt(hDC, 0, 0, width, height, hScreen, 0, 0, SRCCOPY);
// clean up
SelectObject(hDC, old_obj); // selects hBitmap out of hDC
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap); // clipboard now owns the bitmap
CloseClipboard();
If you still have a problem after those changes, I would check the return value of the SetClipboardData call. If that's failing, GetLastError may give a clue.

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.

Capture game window using wm_paint

I'm trying to capture a game window using SendMessage with wm_paint and wm_printclient.
I already did it successfully using PrintWindow but the game can change between graphic engines and for some of them I get a white rectangle. I was hoping using SendMessage would not have this problem.
The problem is I'm getting a black rectangle as result of SendMessage, for any graphic engine and even for any program/window.
void capture::captureProgramScreen(HWND hwnd, tImage* res)
{
RECT rc;
GetWindowRect(hwnd, &rc);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top);
res->width = rc.right - rc.left - 17;
res->height = rc.bottom - rc.top - 39;
res->absoluteTop = rc.top;
res->absoluteLeft = rc.left;
SelectObject(hdc, hbmp);
SendMessage(hwnd, WM_PRINTCLIENT, (int)hdc, PRF_CHILDREN | PRF_CLIENT | PRF_ERASEBKGND | PRF_NONCLIENT | PRF_OWNED);
BITMAPINFO MyBMInfo = { 0 };
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
if (0 == GetDIBits(hdc, hbmp, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS))
{
res->error = true;
res->errorcode = 2;
return;
}
res->v = std::vector<BYTE>(MyBMInfo.bmiHeader.biSizeImage);
MyBMInfo.bmiHeader.biBitCount = 32;
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biHeight = abs(MyBMInfo.bmiHeader.biHeight);
if (0 == GetDIBits(hdc, hbmp, 0, MyBMInfo.bmiHeader.biHeight, &(res->v[0]), &MyBMInfo, DIB_RGB_COLORS))
{
res->error = true;
res->errorcode = 3;
res->width = 0;
res->height = 0;
res->v.clear();
return;
}
//4 Bytes per pixel order (B G R A) from [left to right] [bottom to top]
return;
}
Thank you!
There are at least a few possible issues:
Not all programs/windows implement WM_PRINTCLIENT. Many games don't even implement WM_PAINT, as they draw continuously at their desired frame rate rather than in response to a need to update themselves. Many games use newer graphics APIs that don't really draw to a Device Context.
I'm not sure why you have two calls to GetDIBits. The first one happens before you initialize all the fields of the BITMAPINFO, so that one will fail. It's still not completely filled out by the time you make the second call.