C++ Take a Screenshot [closed] - c++

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I'm new to C++ and have to take a screenshot. I think I have set up everything besides the print method itself and that's where I'm struggling.
I found a post on so which describes how to take a screenshot, but it somehow doesn't work for me. (How to capture part of the screen and save it to a BMP?)
My Method looks like this:
STDOVERRIDEMETHODIMP VImplPrintable::Print(HDC hdc, CRect* pCr)
{
HDC hdcSource = GetDC(NULL);
HDC hdcMemory = CreateCompatibleDC(hdcSource);
int capX = GetDeviceCaps(hdcSource, HORZRES);
int capY = GetDeviceCaps(hdcSource, VERTRES);
HBITMAP hBitmap = CreateCompatibleBitmap(hdcSource, pCr->Width(), pCr->Height());
HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMemory, hBitmap);
BitBlt(hdcMemory, 0, 0, pCr->Width(), pCr->Height(), hdcSource, pCr->top, pCr->left, SRCCOPY);
DeleteDC(hdcSource);
DeleteDC(hdcMemory);
return S_OK;
}
Problem is the screenshot seems to be an empty bitmap.
I don't really know if I it makes sense to create a new HDC when I'm
already getting one as a parameter. Any help is appreciated.

I cannot test because I do not know what is the framework that calls that method, but as you receive the HDC you want to write to, you simply should not use a memory DC and directly BitBlt there. But you should also test the return value of WinAPI calls to return error conditions to the caller:
STDOVERRIDEMETHODIMP VImplPrintable::Print(HDC hdc, CRect* pCr)
{
HDC hdcSource = GetDC(NULL);
if (NULL == hdcSource) return E_FAIL;
HRESUL cr = S_OK;
if (!BitBlt(hdc, 0, 0, pCr->Width(), pCr->Height(), hdcSource, pCr->top, pCr->left,
SRCCOPY)) cr = E_FAIL;
DeleteDC(hdcSource);
return cr;
}

Related

Where is the memory leak from my hdc/hbitmap?

so I've noticed that part of my code leaks a lot of memory when it's called and I've tried to find out where or why it leaks but I'm at a dead end.
I've tried the Visual Studio 2017 debugger to take snapshots to find out where the leak happens but according to that there aren't any major leaks. I've also tried Deleaker which I got working once which told me I leaked HDC and HBITMAP but couldn't tell me how much memory.
The first function is the GetScreenBmp where the leak might be, but aren't I releasing everything properly? I know I'm not deleting hBitmap but I need to return that. Is that where the memory leak is?
HBITMAP GetScreenBmp(HDC hdc, int screenPositionX, int screenPositionY, int screenSizeX, int screenSizeY) {
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
int nMousePositionX = 0, nMousePositionY = 0;
// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
HDC hCaptureDC = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, screenSizeX, screenSizeY);
HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
BOOL bOK = BitBlt(hCaptureDC, 0, 0, screenSizeX, screenSizeY, hdc, screenPositionX, screenPositionY, SRCCOPY | CAPTUREBLT);
SelectObject(hCaptureDC, hOld); // always select the previously selected object once done
DeleteObject(hOld);
DeleteDC(hCaptureDC);
return hBitmap;
The second part is this piece of code, which I'm not entirely sure if I'm deleting everything properly.
HDC hdc = GetDC(0);
HBITMAP hBitmap = GetScreenBmp(hdc, currentSplitInformationArray.screenPositionX, currentSplitInformationArray.screenPositionY, currentSplitInformationArray.screenSizeX, currentSplitInformationArray.screenSizeY);
BITMAPINFO MyBMInfo = { 0 };
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Get the BITMAPINFO structure from the bitmap
if (0 == GetDIBits(hdc, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
MessageBox(NULL, "Resource not available\nDo you want to try again?", "Account Details", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);
}
// create the bitmap buffer
BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];
// Better do this here - the original bitmap might have BI_BITFILEDS, which makes it
// necessary to read the color table - you might not want this.
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biHeight = currentSplitInformationArray.screenSizeY * -1;
// get the actual bitmap buffer
if (0 == GetDIBits(hdc, hBitmap, 0, currentSplitInformationArray.screenSizeY, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
MessageBox(NULL, "Resource not available\nDo you want to try again?", "Account Details", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);
}
::SendMessage(testingComparison, STM_SETIMAGE,
(WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
DeleteObject(&MyBMInfo);
DeleteObject(hBitmap);
ReleaseDC(NULL, hdc);
delete[] lpPixels;
I'm sorry in advance if this is something that has been answered before or if the answer is easily googable, but I've been trying for a few hours to fix it.
Okay I found the solution. The STM_SETIMAGE message returns the previous image and you have to handle it yourself. https://learn.microsoft.com/en-us/windows/desktop/Controls/stm-setimage
I should probably learn to better read the documentation next time, sorry for wasting everyone's time with this one.
::SendMessage(testingComparison, STM_SETIMAGE,
(WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
Fixed it by simply doing
HBITMAP oldBitmap = (HBITMAP)::SendMessage(testingComparison, STM_SETIMAGE,
(WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
DeleteObject(oldBitmap);
Use tools to track your leaks/allocations (btw. you didn't post how you found the leaks in the first place).
Since you are using visual studio c++ you may use the built-in tools. Basicly a combination of the these 3 lines can get the job done.
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );//to enable, safe to always set somewhere around program startup - on normal exit this will print whatever you leaked
//_CrtDumpMemoryLeaks();//Dumps to see what has already been allocated
//_CrtSetBreakAlloc(#number);//Use this to set breakpoint using the allocation number from heap dump to see where allocation takes place. If allocation happends before this line it will not work.

hdc is undefined [gdi+, mfc standard application]

So my teacher gave us a chunk of code to use for double buffering.
He said "here, use this code so you don't have to sit there for hours finding out how"
Except his code does not function.
His initial usage of hdc is undefined. I tried putting it in the parameter list but that is a no go.
This is the code he gave us:
// Create a backbufer bmp bufer to draw to in memory.
RECT rcClient;
::GetClientRect(hwnd, &rcClient);
int left = rcClient.left;
int top = rcClient.top;
int width = rcClient.right - rcClient.left;
int height = rcClient.bottom - rcClient.top;
HDC hdcMem = ::CreateCompatibleDC(hdc);
const int nMemDC = ::SaveDC(hdcMem);
HBITMAP hBitmap = ::CreateCompatibleBitmap(hdc, width, height);
::SelectObject(hdcMem, hBitmap);
Graphics graphics(hdcMem);
SolidBrush back(Color(255,255,255));
graphics.FillRectangle(&back, left, top, width, height);
// Draw to backbufer bitmap here.
// End draw to backbufer bitmap bufer.
// Swap bufers ie. push memory backbufer to the screen frontbufer
RECT rcClip;
::GetClipBox(hdc, &rcClip);
left = rcClip.left;
top = rcClip.top;
width = rcClip.right - rcClip.left;
height = rcClip.bottom - rcClip.top;
::BitBlt(hdc, left, top, width, height, hdcMem, left, top, SRCCOPY);
::RestoreDC(hdcMem, nMemDC);
::DeleteObject(hBitmap);
Right here is where I run into the errors: HDC hdcMem = ::CreateCompatibleDC(hdc);
I attempted declaring an HDC like so
HDC hdc = (HDC)BeginPaint((LPPAINTSTRUCT)AfxGetApp()->m_pMainWnd->GetSafeHwnd());
But that doesn't compile. What do I do with this hdc?
The HDC is returned by BeginPaint, which presumably is called immediately before this code. BeginPaint takes two parameters and you are trying to call it with only one parameter. Do you have earlier exercises where you handled BeginPaint?
So thanks to the various answers here. My knowledge of this is still fairly new, however with the help of my class mates I was able to come to a solution. Sadly I still do not know what to do with the HDC and this was the first of my problems,
HDC hdcMem = ::CreateCompatibleDC(hdc); was able to be replaced by
HDC hdcMem = ::CreateCompatibleDC(dc);
and a lot of the other code that produced warnings such as the hwnd was simply removed and it worked fine.

Memory leaks in c++ dll [duplicate]

This question already has answers here:
Why does CreateCompatibleBitmap fail after about a thousand executions?
(1 answer)
Capture screenshot Including Semitransparent windows in .NET
(1 answer)
Closed 9 years ago.
I have simple c++ dll that contains code for screen capturing.
HBITMAP hCaptureBitmap;
extern "C" __declspec(dllexport) HBITMAP __stdcall CaptureScreenByGDI(bool allScreens)
{
int nScreenWidth;
int nScreenHeight;
HDC hDesktopDC;
if(allScreens)
{
nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
}
else
{
nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
}
HWND hDesktopWnd = GetDesktopWindow();
if(allScreens)
{
hDesktopDC = GetDC(NULL);
}
else
{
hDesktopDC = GetDC(hDesktopWnd);
}
HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
hCaptureBitmap =CreateCompatibleBitmap(hDesktopDC,
nScreenWidth, nScreenHeight);
SelectObject(hCaptureDC,hCaptureBitmap);
BitBlt(hCaptureDC,0,0,nScreenWidth,nScreenHeight,
hDesktopDC,0,0,SRCCOPY|CAPTUREBLT);
ReleaseDC(hDesktopWnd,hDesktopDC);
DeleteDC(hCaptureDC);
return hCaptureBitmap;
}
extern "C" __declspec(dllexport) void __stdcall ClearAfterGDI()
{
DeleteObject(hCaptureBitmap);
}
After calling
CaptureScreenByGDI(true);
ClearAfterGDI();
from c# still remain memory leaks. Why?
If I call DeleteObject(hCaptureBitmap) from CaptureScreenByGDI function and return void everthing is OK.
How to solve this?
You need to save the old bitmap in the DC you create and restore it before deleting the DC:
HGDIOBJ hBmpOld = SelectObject(hCaptureDC,hCaptureBitmap);
...
SelectObject(hCaptureDC, hBmpOld);
DeleteDC(hCaptureDC);

Strange error with CreateCompatibleDC

Maybe this is a foolish question, I can't see why I can not get a DC created in the following code :
HBITMAP COcrDlg::LoadClippedBitmap(LPCTSTR pathName,UINT maxWidth,UINT maxHeight)
{
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, pathName, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (!hBmp)
return NULL;
HDC hdc = (HDC)GetDC();
HDC hdcMem = CreateCompatibleDC(hdc);
if (!hdcMem)
{
DWORD err = GetLastError();
}
...
...
...
The bitmap hBmp is loaded fine and hdc has a valid value. But the call to CreateCompatibleDC() returns a NULL pointer. Then, GetLastError() returns 0 !
Anybody can guess what's going on here , please ?
PS : There are no memory allocations or GDI routines called before this one...so I think memory leaks should be ruled out.
You are improperly casting the result of GetDC() to an HDC. GetDC() returns a pointer to a CDC object.
To do what you want you can do either of the following. The first choice fits more into how MFC likes to do things, but both work just fine:
CDC *pDC = GetDC();
// Option 1
CDC memDC;
memDC.CreateCompatibleDC(pDC);
// Option 2
HDC hMemDC = CreateCompatibleDC((HDC)(*pDC));
It is important to note that option 2 does not do the same thing that you're currently doing wrong. The CDC class has an operator HDC() member that allows it to be converted to an HDC, but this does NOT apply to the pointer. You must dereference it first.
Certain device contexts won't work with CreateCompatibleDC(). The DC has to support raster operations. You can feed the hdc to GetDeviceCaps() and check RASTERCAPS.
But it turns out the GetDC you're calling is a method on a class and not the C binding I thought you meant. SoapBox has the right answer.

CreateDIBSection is throwing, when trying to create cursor on Win

I am trying to create Cursor from png, and CreateDIBSection() is throwing.
Follwoing is the snippet of code:
HDC hdc = GetDC(NULL);
void* lpBits = NULL;
HBITMAP hBitmap;
try
{
hBitmap = CreateDIBSection(
hdc,
(BITMAPINFO*)&bi,
0,
&lpBits,
NULL,
(DWORD)0);
}
ReleaseDC(NULL, hdc);
As CreateDIBSection is throwing, the code to release DC is not getting executed. can you please let me know the possible issue behind this?
You should make structure zeroed out:
ZeroMemory(&bi,sizeof(BITMAPV5HEADER));
Try out this link , it may help you :
http://support.microsoft.com/kb/318876