Convert PrintWindow to BitBlt to capture screenshot of a specific window - c++

I have a C++ program to capture the screenshot of a specific window and save it using the following code
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
RECT rc;
HWND hwnd = FindWindow(NULL,TEXT("Window Title Here"));
if(hwnd == NULL)
{
cout<<"Can't Find Window";
return 0;
}
GetClientRect(hwnd,&rc);
HDC hdcscreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcscreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcscreen,rc.right - rc.left,rc.bottom - rc.top);
SelectObject(hdc,hbmp);
PrintWindow(hwnd,hdc,NULL);
BitmapToJpg(hbmp,rc.right - rc.left,rc.bottom-rc.top); //Function to convert hbmp bitmap to jpg
DeleteDC(hdc);
DeleteObject(hbmp);
ReleaseDC(NULL,hdcscreen);
}
This code works for many of the windows, but for some of the windows, the output is a black image with correct width and height. On searching I found a solution to use BitBlt(). But I cannot figure out how to replace PrintWindow() with BitBlt() and output to a HBITMAP. Help Need

First, replace hdcscreen by hdcwnd, which you get with GetDC(hwnd) instead of GetDC(NULL). It probably won't change anything, but it is more adequate, even with PrintWindow().
Then, just replace :
PrintWindow(hwnd,hdc,NULL);
By :
BitBlt( hdc, 0, 0, rc.right - rc.left,rc.bottom-rc.top, hdcwnd, 0, 0, SRCCOPY );

Related

Why do i need this curly braces here? Can anybody explain me why does this happens?

I'm making a code to make a screenshot and save it in a JPEG file type. I found this piece of code but I don't understand why it gives me an error when I remove curly braces after
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);.
Full code:
void gdiscreen()
{
using namespace Gdiplus;
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
{
HDC scrdc, memdc;
HBITMAP membit;
scrdc = ::GetDC(0);
int Height = GetSystemMetrics(SM_CYSCREEN);
int Width = GetSystemMetrics(SM_CXSCREEN);
memdc = CreateCompatibleDC(scrdc);
membit = CreateCompatibleBitmap(scrdc, Width, Height);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(memdc, membit);
BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);
Gdiplus::Bitmap bitmap(membit, NULL);
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
bitmap.Save(L"screen.jpeg", &clsid);
SelectObject(memdc, hOldBitmap);
DeleteObject(memdc);
DeleteObject(membit);
::ReleaseDC(0, scrdc);
}
GdiplusShutdown(gdiplusToken);
}
Can somebody explain to me why I need the curly braces?
And when I remove the curly braces it gives me the following error:
Exception produced in 0x661AF6B8 (GdiPlus.dll) in DebugScreenShotModule.exe: 0xC0000005: Access violation when reading location 0x029E12AC.
You have a variable Gdiplus::Bitmap bitmap declared within the curly braces. It will be destroyed at the closing }. Without the curly braces, it won't be destroyed until after GdiplusShutdown is called.

Loading Windows screenshot into SFML Image

In C++ code, I need to take a screenshot of a given window, store the pixels in memory, and then load them into an SFML Image object. So far, I have a handle to the window I need to screenshot and can create an HBITMAP object of it:
HWND window = FindWindow(TEXT("WindowName"), NULL);
RECT rc;
GetClientRect(window , &rc);
HBITMAP hbmp;
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
hbmp = CreateCompatibleBitmap(hdcScreen,
rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdc, hbmp);
PrintWindow(window, hdc, PW_CLIENTONLY);
GetObject(hbmp, sizeof(BITMAP), bitmap);
Now to be honest, I'm not really much of a Windows guy and I don't understand what's really happening with this WinAPI stuff here, this is just some code I found online to get screenshots. What I need to do is get this into some type of standard format that I can pass into SFML's function:
sf::Image::loadFromMemory(const void * data, std::size_t size)
And preferably something that will also be easy to send over a socket connection later. How can I get a nice, non-Windows-specific format for my screenshot?

Get a window snapshot and crop it

I have the following code working properly, it takes a snapshot of the active window on my app, puts it in a HBITMAP variable and saves it in a file.
Now I would like to be able to crop the image and save only a portion of it according to given start coordinates and width/height.
An important point is that I have to save the window with the title bar, not just the client area, so it was easy to achieve with PrintWindow() rather than the BitBlt() approach.
I prefer a solution that will use PrintWindow(), because the BitBlt() approach doesn't take the title bar properly (unless you know the way of doing that).
The current code that works properly for the whole window is:
HWND hParentWindow = GetActiveWindow();
RECT rc;
GetWindowRect(hParentWindow, &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
//create
HDC hdcParent = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcParent);
HBITMAP hBmp = CreateCompatibleBitmap(hdcParent, width, height);
SelectObject(hdc, hBmp);
//Print to memory hdc
PrintWindow(hParentWindow, hdc, 0);
//copy to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBmp);
CloseClipboard();
// Save it in a file:
saveBitmap(ofn.lpstrFile, hBmp);
//release
DeleteDC(hdc);
DeleteObject(hBmp);
ReleaseDC(NULL, hdcParent);
How can I save the bitmap cropped?
Essentially do a BitBlt. Here is a thread discussing this issue with a solution that appears to be adequate for your needs here:
Crop function BitBlt(...)
create another intermediate hdc
print the window to this intermediate hdc.
copy (bitblt) the rect you need from this hdc to your bitmap hdc
relase the intermediate hdc

Windows API Rendering an Image From File (Beginner)

I am trying to understand how to load and render an image from a file using the Windows API, Direct2D, and Visual C++. I have been more or less attempting to follow an MSDN article on this topic. I am new to both C++ (experienced in C) and the Windows API.
I wrote 3 functions.
HRESULT imagefactorysetup(IWICImagingFactory * pImageFactory)
{
HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID *) &pImageFactory);
return hr;
}
HRESULT imageload(LPCWSTR filename, IWICImagingFactory * pImageFactory, IWICBitmapFrameDecode * pFrame)
{
IWICBitmapDecoder * pDecoder = NULL;
HRESULT hr = pImageFactory->CreateDecoderFromFilename(filename, NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &pDecoder);
if (SUCCEEDED(hr))
hr = pDecoder->GetFrame(0, &pFrame);
//Format convert the frame to 32bppPBGRA
IWICFormatConverter * pFormatConverter = NULL;
if (SUCCEEDED(hr))
{
SafeRelease(&pFormatConverter);
hr = pImageFactory->CreateFormatConverter(&pFormatConverter);
}
if (SUCCEEDED(hr))
hr = pFormatConverter->Initialize(pFrame, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeCustom);
return hr;
}
HRESULT imagerender(HWND hWnd, IWICBitmapFrameDecode * pFrame, int x, int y)
{
//Create a D2D render target properties
D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties = D2D1::RenderTargetProperties();
//Set the DPI to be the default system DPI to allow direct mapping
//between image pixels and desktop pixels in different system DPI settings
renderTargetProperties.dpiX = DEFAULT_DPI;
renderTargetProperties.dpiY = DEFAULT_DPI;
//Create a D2D render target
D2D1_SIZE_U sz = D2D1::SizeU(MAINWINDOWWIDTH, MAINWINDOWHEIGHT); //Change size
ID2D1Factory * pD2DFactory;
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory1), NULL, (LPVOID *) &pD2DFactory);
ID2D1RenderTarget * pRenderTarget;
//renderTargetProperties, D2D1::HwndRenderTargetProperties(hWnd, sz), &pRenderTarget);
hr = pD2DFactory->CreateHwndRenderTarget(&renderTargetProperties, D2D1::HwndRenderTargetProperties(hWnd, sz), &pRenderTarget);
ID2D1Bitmap * pD2DBitmap = NULL;
pRenderTarget->CreateBitmapFromWicBitmap(pFrame, NULL, &pD2DBitmap);
D2D1_SIZE_F size = pD2DBitmap->GetSize();
D2D1_POINT_2F origin = D2D1::Point2F((float) x, (float) y);
if (pD2DBitmap)
pRenderTarget->DrawBitmap(pD2DBitmap, D2D1::RectF(origin.x, origin.y, origin.x + size.width, origin.y + size.height));
return hr;
}
Question:
1) The following line gives me an error. I tried reading some documentation on MSDN but am unsure what the issue is.
hr = pD2DFactory->CreateHwndRenderTarget(&renderTargetProperties, D2D1::HwndRenderTargetProperties(hWnd, sz), &pRenderTarget);
Error:
IntelliSense: no instance of overloaded function "ID2D1Factory::CreateHwndRenderTarget" matches the argument list
argument types are: (D2D1_RENDER_TARGET_PROPERTIES *, D2D1_HWND_RENDER_TARGET_PROPERTIES, ID2D1RenderTarget **)
object type is: ID2D1Factory 68 18
2) In general, is there a more idiomatic / efficient way of approaching the problem of rendering an image from a file onto a window than what I have attempted? My previous programming experience has been strictly in C.
No problem. You can use GDI+ to load any image that windows supports natively.
You can then use GDI to draw it.
Here's a short example of drawing a (transparent) PNG to the background of a dialog. I've built it using MinGW32 and Code::Blocks. You'll need to link the msimg32 and gdiplus libraries to make use of AlphaBlend and the Bitmap class (and the functions to init/shutdown GDI+).
Points that may be worth mentioning are:
mLoadImage will load anything that windows will show in Windows Photo
Viewer (// BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF) - it uses
the Bitmap class, as found in Gdiplus.
The WM_ERASEBKGND message comes with wParam holding a device context
that you can draw straight into - that's why there's no need to get
one by usig BeginPaint, as we do in response to a WM_PAINT message.
You can use BitBlt or StretchBlt for images that dont contain transparent areas.
Main.cpp
#define WINVER 0x0500 // for AlphaBlend
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <gdiplus.h>
#include "resource.h"
using namespace Gdiplus;
HINSTANCE hInst;
void setClientSize(HWND mHwnd, int width, int height)
{
RECT wndRect, clientRect, mRect;
int clientX, clientY, windowX, windowY, difX, difY;
GetWindowRect(mHwnd, &wndRect);
GetClientRect(mHwnd, &clientRect);
clientX = clientRect.right - clientRect.left;
clientY = clientRect.bottom - clientRect.top;
windowX = wndRect.right - wndRect.left;
windowY = wndRect.bottom - wndRect.top;
difX = windowX - clientX;
difY = windowY - clientY;
// GetWindowRect(mHwnd, &mRect);
POINT topLeft = {wndRect.left, wndRect.top};
// ScreenToClient(mParentHwnd, &topLeft);
SetWindowPos(mHwnd, HWND_TOP, topLeft.x, topLeft.y, width+difX, height+difY, SWP_NOZORDER);
}
HBITMAP mLoadImg(wchar_t *filename)
{
Bitmap mBitmap(filename,false);
HBITMAP result;
mBitmap.GetHBITMAP(0x00000000, &result);
return result;
}
void onPaint(HWND hwnd, HBITMAP bkg)
{
HDC memDC, hdc;
PAINTSTRUCT ps;
HBITMAP old;
RECT clientRect;
int width, height;
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &clientRect);
width = clientRect.right - clientRect.left;
height = clientRect.bottom - clientRect.top;
memDC = CreateCompatibleDC(NULL);
old = (HBITMAP)SelectObject(memDC, bkg);
byte alpha = 255;
BLENDFUNCTION bf = {AC_SRC_OVER,0,alpha,AC_SRC_ALPHA};
AlphaBlend(hdc, 0,0,width,height, memDC, 0,0, width,height, bf);
// try the below instead of AlphaBlend - they each rely on the fact I've resized the
// client area to the same size as the image I'll draw on it.
// BitBlt(hdc, 0,0, clientRect.right,clientRect.bottom, memDC, 0,0, SRCCOPY);
SelectObject(memDC, old);
DeleteDC(memDC);
EndPaint(hwnd, &ps);
}
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HBITMAP mBkg;
switch(uMsg)
{
case WM_INITDIALOG:
{
mBkg = mLoadImg(L"wiki.png");
BITMAP bm;
GetObject(mBkg, sizeof(bm), &bm);
setClientSize(hwndDlg, bm.bmWidth, bm.bmHeight);
}
return TRUE;
case WM_ERASEBKGND:
{
RECT clientRect;
HBRUSH bkgBrush = CreateSolidBrush( RGB(255,0,0) );
GetClientRect(hwndDlg, &clientRect);
FillRect( (HDC)wParam, &clientRect, bkgBrush);
DeleteObject(bkgBrush);
}
return 1;
case WM_PAINT:
onPaint(hwndDlg, mBkg);
return 0;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hInst=hInstance;
InitCommonControls();
int retVal = DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
GdiplusShutdown(gdiplusToken);
return retVal;
}
resource.h
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif
#define DLG_MAIN 100
resource.rc
// Generated by ResEdit 1.6.2
// Copyright (C) 2006-2014
// http://www.resedit.net
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"
//
// Dialog resources
//
DLG_MAIN DIALOG 0, 0, 186, 95
STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_THICKFRAME | WS_SYSMENU
EXSTYLE WS_EX_WINDOWEDGE
CAPTION "Dialog"
FONT 8, "Ms Shell Dlg"
{
}
Wiki.png
Result

Getting a Bitmap Object of a screenshot in VC++

I'm trying to write a program that grabs a screen shot of the current active window and then does some basic computer vision with the result. The current code I have is as follows:
RECT rc;
HWND hwnd = GetForegroundWindow(); //the window can't be min
if(hwnd == NULL) {
cerr << "it can't find any 'note' window" << endl;
system ("pause");
return 0;
}
GetClientRect(hwnd, &rc);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdc, hbmp);
//Print to memory hdc
PrintWindow(hwnd, hdc, PW_CLIENTONLY);
//Get the bitmap
BITMAP bm;
GetObject (hbmp, sizeof(bm), &bm);
//release
DeleteDC(hdc);
DeleteObject(hbmp);
ReleaseDC(NULL, hdcScreen);
After everything's said an done, bm.bmBits is NULL. This is especially strange because all of bm's other fields are correct. Obviously I can't do very much without bm.bmBits. Could anyone tell me what I'm doing wrong?
This code is mostly copied from the answer to another question, but in that code handle to a bitmap was never transferred into a BITMAP struct, but instead was used to add the bitmap to the clipboard. Since I want to process the screenshot immediately, I'd rather extract the data within the handle immediately and process it.
bm.bmBits is NULL because you don't own the memory referenced by the bitmap. You need to have the kernel copy them for you, using GetDIBits or the like.