CImage destructor issue? - c++

I am using the function below to process an image in real time. The function is called every 10 seconds using a timer.
The problem is that I get an assertion failure , and can't figure out the exact problem. I tried CImage::ReleaseDC() and DeleteDC() for the ImageDC but no luck.
Any ideas ?
LRESULT CAutodetectDialog::AutoscanPatterns(WPARAM, LPARAM)
{
HWND hwnd = ::FindWindow(NULL, windowTitle);
if (hwnd != NULL)
for (int i=0; i<N_NUMBERS; i++)
{
CImage image;
image.Create(dbParams.width, dbParams.height, 24);
CImageDC imageDC(image);
::SetWindowOrgEx(imageDC, db.topLeft.x, dbParams.topLeft.y + i * dbParams.height, NULL);
::PrintWindow(hwnd, imageDC, PW_CLIENTONLY);
// Process the image - processing takes < 1 sec
// and the image parameter is not being changed
SaveImagePatterns(&image);
} // <------------- This line fails , must be the destructor
// of CImage : atlimage.h Line 884, m_hDC == 0
// m_hDC is not NULL in the code
return 0;
}
// Process the image - processing takes < 1 sec
// and the image parameter is not changed
void CAutodetectDialog::SaveImagePatterns(const CImage* image)
{
.........
}
This is the code that fails in atlimage.h :
inline HBITMAP CImage::Detach() throw()
{
HBITMAP hBitmap;
ATLASSUME( m_hBitmap != NULL );
ATLASSUME( m_hDC == NULL ); // <------ This guy
hBitmap = m_hBitmap;
...
...
return( hBitmap );
}
UPDATE : After commenting out the call to function SaveImagePatterns() , the assertion failure did not happen. So the problem must be in that function, despite the CImage param passed as const.

This looks suspicious:
SaveImagePatterns(&image);
Since image is a local variable, depending on what SaveImagePatterns does with it, this can cause an issue since the image object is destroyed as soon as that block is exited.

Did you call any image->GetDC() by yourself in SaveImagePatterns?
Mind that image->GetDC() need to be paired with image->ReleaseDC().
So that m_hDC will be NULL.

Related

C++ Win32. SelectObject Fails, with GetLastError returning error 1400 (invalid window handle)

So I'm trying to make a clone of Pong in Win32, and things WERE working, but then I did a lot of stuff with the physics, and when I tested it, the sprite bitmaps weren't even displaying any more :/
So, here is how I initialise the rendering stuff:
int InitRenderer(int showCMD)
{
context = GetDC(winHandle);
if(!context)
{
return EXIT_FAILURE;
}
ShowWindow(winHandle, showCMD);
UpdateWindow(winHandle);
CreateDoubleBuffer(&globalBuffer);
ClearWindow(globalBuffer.hdcBack, globalBuffer.scrnRect);
return EXIT_SUCCESS;
}
Here is the CreateDoubleBuffer function:
void CreateDoubleBuffer(BUFFER *buffer)
{
buffer->hwnd = winHandle;
GetClientRect(winHandle, &(buffer->scrnRect));
buffer->hdcFront = GetDC(buffer->hwnd); //get a handle to the DC and plop it into the front buffer.
buffer->hdcBack = CreateCompatibleDC(buffer->hdcFront); //get a compatible DC for the Back buffer.
buffer->hdcBitmap = CreateCompatibleDC(buffer->hdcFront); //get a compatible DC for the bitmap.
buffer->hCompBitmap = CreateCompatibleBitmap(buffer->hdcFront, buffer->scrnRect.right, buffer->scrnRect.bottom); //Create a compatible bitmap as a dummy, and store in the front buffer.
buffer->hOldBitmap = (HBITMAP)SelectObject(buffer->hdcBack, buffer->hCompBitmap);
}
the BUFFER struct, for reference, looks like this:
struct BUFFER // This is our back buffering structure
{
HWND hwnd; // This holds the current window's handle
RECT scrnRect; // This holds the client rectangle of the window
HANDLE hCompBitmap; // This holds the compatible bitmap for the backbuffer
HANDLE hOldBitmap; // This is used for storage to free when the program quits
HANDLE hOldBitmap2; // This is used as storage to swap between selected bitmaps when using selectObject()
HDC hdcFront; // This is the front buffer (The part we see)
HDC hdcBack; // This is the back buffer (the part we draw to, then flip)
HDC hdcBitmap; // This is a temp buffer to swap the bitmap back and forth from
};
So I have a Sprite class which just wraps an HBITMAP and a string for the filename, and some functions to manipulate those. When I want to draw the sprite, this function is called:
void RenderSprite(BUFFER *buffer, HBITMAP bmp, Vec2I pos, Vec2F origin)
{
buffer->hOldBitmap2 = (HBITMAP)SelectObject(buffer->hdcBitmap, bmp); //we put the bitmap into the extra HDC to hold it there.
if(!buffer->hOldBitmap2)
{
std::cout << GetLastError() << "\n";
}
BitBlt(buffer->hdcBack, pos.GetX() + (int)origin.GetX(), pos.GetY() + (int)origin.GetY(), buffer->scrnRect.right, buffer->scrnRect.bottom, buffer->hdcBitmap, 0, 0, SRCCOPY); //blit the bitmap into the backbuffer.
SelectObject(buffer->hdcBitmap, buffer->hOldBitmap2); //put the old handle to the bitmap back where it belongs.
}
And it is at the start of this function where SelectObject fails, so buffer->hOldBitmap2 is null. The error returned by GetLastError is 1400, which means invalid window handle, so I guess winHandle (a global variable, just so you know) is messed up. But I don't see how. Here is how I initialise it:
int Game::Start(HINSTANCE instance, int showCMD)
{
WNDCLASSEX winClass = {0};
winClass.cbSize = sizeof(winClass);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = WndProc;
winClass.hInstance = instance;
winClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winClass.lpszClassName = _winClassName;
winClass.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(OCR_CROSS), IMAGE_CURSOR, 0, 0, LR_SHARED); //using a cross for a cursor because we're hipsters here at cow_co industries.
RegisterClassEx(&winClass);
/**
* WS_EX_CLIENTEDGE gives the client a sunken edge.
**/
winHandle = CreateWindowEx(WS_EX_CLIENTEDGE, _winClassName, "Win32 Pong", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, instance, NULL);
if(!winHandle)
{
return EXIT_FAILURE;
}
//other stuff...
And I haven't changed this since the time when it worked.
In terms of what happens on-screen, I just get a blank white window, so no Crash message or whatever, but it's just...blank.
I've looked around other questions and the solutions to those seem to be related to issues registering the window class etc. I've checked mine and I can't see anything wrong with it. I've debugged, and hOldBitmap2 is the only part of the buffer that's null. The rest's fine.
Any help you sages of the Stack could provide would be much appreciated.
I just ran into this problem.
An application cannot select a single bitmap into more than one DC at
a time. Is the bitmap already selected into another DC? – theB Aug 27
'15 at 18:09
Is close!
OK:
SelectObject( hdc_bitmap, buf_bitmap )
FAILS:
SelectObject( hdc_bitmap, buf_bitmap )
SelectObject( hdc_bitmap, buf_bitmap )
This means if your RenderSprite function tries to draw the same sprite twice in a row,
it will fail.

GetTextExtentPoint32(m_hAttribDC, lpszString, nCount, &size))

My program is to display some points with their positions(x,y) on a graph. When I use mouse to drag any point, its position will automatically changed. Updated position is implemented following this code(using thread):
m_thread =AfxBeginThread((AFX_THREADPROC)MainThread,this)
UINT CAtwWnd::MainThread(LPVOID pParam)
{
CAtwWnd *pMainDlg = (CAtwWnd*)pParam;
static SChartXYPoint pPoint;
TCHAR strTemp[32]={0,};
while(1)
{
pMainDlg->m_chart.EnableRefresh(false);
pMainDlg->InitGraph1();
wsprintf(strTemp, _T("[%d](%d,%d)"), (int)index,(int)xPoint,(int) yPoint);
pBalloon[index]->SetLabelText(strTemp);
pBalloon[index] = pMainDlg->m_pPointSeries->CreateBalloonLabel(index, strTemp);
pBalloon[index]->SetVisisble(true);
pMainDlg->m_pPointSeries->SetVisible(true);
pMainDlg->m_chart.EnableRefresh(true);
pMainDlg->SetAtwGraphStep(1);
}
return 0;
}
Mean while:
void CChartLabel<PointType>::SetLabelText(const TChartString& strText)
{
m_strLabelText = strText;
m_pParentCtrl->RefreshCtrlAtw();
}
And:
void CChartCtrl::RefreshCtrlAtw()
{
// Window is not created yet, so skip the refresh.
if (!GetSafeHwnd())
return;
if (m_iEnableRefresh < 1)
{
m_bPendingRefresh = true;
return;
}
// Retrieve the client rect and initialize the
// plotting rect
CClientDC dc(this) ;
CRect ClientRect;
GetClientRect(&ClientRect);
m_PlottingRect = ClientRect;
// If the backgroundDC was not created yet, create it (it
// is used to avoid flickering).
if (!m_BackgroundDC.GetSafeHdc() )
{
CBitmap memBitmap;
m_BackgroundDC.CreateCompatibleDC(&dc) ;
memBitmap.CreateCompatibleBitmap(&dc, ClientRect.Width(),ClientRect.Height()) ;
m_BackgroundDC.SelectObject(&memBitmap) ;
}
// Draw the chart background, which is not part of
// the DrawChart function (to avoid a background when
// printing).
DrawBackground(&m_BackgroundDC, ClientRect);
ClientRect.DeflateRect(3,3);
DrawChart(&m_BackgroundDC,ClientRect);
for (int i=0; i<4 ;i++)
{
if (m_pAxes[i])
m_pAxes[i]->UpdateScrollBarPos();
}
Invalidate();
}
when dragging points on graph I gets these errors somtimes: Debug Assertion Failed ( afxwin1.inl, line 639, and 646)
_AFXWIN_INLINE CSize CDC::GetTextExtent(LPCTSTR lpszString, int nCount) const
{
ASSERT(m_hAttribDC != NULL);
SIZE size;
VERIFY(::GetTextExtentPoint32(m_hAttribDC, lpszString, nCount, &size));
return size;
}
_AFXWIN_INLINE CSize CDC::GetTextExtent(const CString& str) const
{
ASSERT(m_hAttribDC != NULL);
SIZE size;
VERIFY(::GetTextExtentPoint32(m_hAttribDC, str, (int)str.GetLength(), &size));
return size;
}
Could you help me to fix this problem? I tried to find some ways to fix but doesn't work. :(
My answer is just a guess, but the reason might be caused by using MFC objects from one thread (the creator) in a second thread. And it is a guess because you didn't told us what the ASSERT say and what VS version you are using.
The problem: When you create some objects in the MFC, the handle values are saved in a map that allows the MFC to find the object only with the handle value.
This handle maps are stored per thread.
Also if a window object stores other objects that are associated with those handle maps, the usage from another thread will fail.
So the answer can be found in the call stack. It tells you who uses such an object. And the object that causes the problem is simply identified by the ASSERT.

Possible memory leak using GetHBITMAP() and MFC CStatic::SetBitmap()

I'm calling the following function every 100ms. It's goal is to take an image from renderBuffer, resize it and display it in the dialog's CStatic control with SetBitmap().
Problem is that I'm observing a rather huge spike of memory usage every second when this function is executed. It displays the resized image in CStatis control without a problem, but I see in task manager that each second process allocates additional 4 megabytes of memory and doesn't ever stop until the process runs out of memory.
Here is the code, let me know if you have any idea what could be the problem.
void CAppDlg::UpdatePreview( const RenderBuffer* renderBuffer )
{
HBITMAP hbmReturn = NULL;
Gdiplus::Bitmap *bmPhoto = NULL;
Gdiplus::Bitmap bmPhoto( THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT );
CBitmap Bmp1;
Gdiplus::Bitmap image( 780, 780, 4*780, PixelFormat32bppARGB, renderBuffer->buffer );
int sourceWidth = image.GetWidth();
int sourceHeight = image.GetHeight();
int destX = 0,
destY = 0;
float nPercent = 0;
float nPercentW = ((float)THUMBNAIL_WIDTH/(float)sourceWidth);;
float nPercentH = ((float)THUMBNAIL_HEIGHT/(float)sourceHeight);
if(nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = (int)((THUMBNAIL_WIDTH - (sourceWidth * nPercent))/2);
}
else
{
nPercent = nPercentW;
destY = (int)((THUMBNAIL_HEIGHT - (sourceHeight * nPercent))/2);
}
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
bmPhoto.SetResolution( image.GetHorizontalResolution(), image.GetVerticalResolution() );
Gdiplus::Graphics *grPhoto = Gdiplus::Graphics::FromImage( &bmPhoto );
Gdiplus::Color colorW(255, 255, 255, 255);
grPhoto->Clear( colorW );
grPhoto->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic );
grPhoto->DrawImage( &image, Gdiplus::Rect(destX, destY, destWidth, destHeight) );
bmPhoto.GetHBITMAP( colorW, &hbmReturn );
m_BitmapPreview.SetBitmap( hbmReturn ); // ---- without this line memory usage doesn't go up rapidly every second.
DeleteObject( hbmReturn ); // ---- returns non-zero which would point out that it was freed properly.
delete grPhoto;
}
Thanks for any help!
Regards.
I guess you should use DeleteObject.
Here is how the code should look like in my opinion:
// ..............
bmPhoto.GetHBITMAP( colorW, &hbmReturn );
HBITMAP prev = m_BitmapPreview.SetBitmap( hbmReturn ); // ---- without this line memory usage doesn't go up rapidly every second.
if (NULL != prev)
{
DeleteObject(prev); // *** do not forget to delete the previously associated bitmap
}
DeleteObject( hbmReturn ); // ---- returns non-zero which would point out that it was freed properly.
// .........

Caching a bitmap

I'm writing a Win32 application using C++.
In this application I'm handling the WM_PAINT message:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
GdiplusStartup(&gdiplusToken, &gdiPlusStartup, 0);
DrawM(ps.hdc, hWnd);
EndPaint(hWnd, &ps);
break;
And in the DrawM function I'm having something like this:
void DrawMap(HDC hdc, HWND hWnd)
{
if(!isDrawn)
{
// (some calculations)
Graphics g(hdc);
Bitmap img(max_x, max_y, &g);
int zoom_factor = 50;
for(int i = 0; i< segments.size(); i++)
{
// (some math)
for(int j = 0; j < segments.at(i).point_count; j++)
// (another dose of math)
g.DrawLines(&pen, segmentPoints, segments.at(i).point_count);
delete [] segmentPoints;
}
g.Save();
isDrawn = true;
}
else
{
// here is the problem
}
}
In the code above, what I wanted to do, is to render the image once, and later on when the window is resized, moved or anything happens to it that requires repainting will not render the Bitmap from scratch, instead it should use a cached one.
The problem is that Bitmap does not allow copying (the copy constructor denies it).
Another problem is that, when I'm trying to save the image to a file or a stream I receive an "Invalid parameter" error (i.e the return code is 2):
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
img.Save(_T("m.png"), &Gdiplus::ImageFormatPNG, NULL);
->clone() also seems that it is not working, because when I define a pointer to a Bitmap, clone the bitmap to it and in the "else" statement I use:
Graphics g(hdc);
g.DrawImage(bmpClone, 50, 50);
Nothing is rendered.
Any ideas on how to cache the Bitmap?
Clone() should work, but without seeing your code (which uses it) it's hard to know what's going on. As an alternative, another (more circuitous) approach would be to call GetHBITMAP() on the original Bitmap, store the GDI bitmap handle and then construct the new Bitmap with the Bitmap(HBITMAP, HPALETTE) constructor in future repaints.
Instead of declaring img as a local object, make it a static or a member of a class. Then it will be available at the next WM_PAINT without needing to be copied.

Shrink the bitmap to required dimension

I have a bitmap of large dimension (2000 x 2000) i need to shrink that bitmap to a small dimension (150 x 150). i have written a code for it, but its not working. Can anybody help in finding the problem? The problem is the destination bitmap is just blank. I am selecting wrong DC's? I have made sure that both the source and destinations are correct. After doing bitblt do i need to do some more thing to destination bitmap?
BOOL ReSizeBitmap(CBitmap *pBitmap, CBitmap *pNewBitmap)
{
// Get new bitmap size
BITMAP bmOld;
if( !pBitmap->GetBitmap(&bmOld) )
{
return FALSE;
}
CRect rcPrev(0, 0, bmOld.bmWidth, bmOld.bmHeight);
int newWidth = 150;
int newHeight = 150;
if( newWidth < 1 || newHeight < 1 )
{
::SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
BOOL bResult = FALSE;
try
{
CDC dcDest;
CDC dcSource;
dcSource.CreateCompatibleDC(NULL);
dcDest.CreateCompatibleDC(NULL);
CBitmap* pSourceOld = dcSource.SelectObject(pBitmap);
CBitmap* pDestold = dcDest.SelectObject(pNewBitmap);
if( !pNewBitmap->CreateCompatibleBitmap(
&dcDest, newWidth, newHeight) )
{
return FALSE;
}
int oldStretchMode = dcDest.SetStretchBltMode(HALFTONE);
bResult = dcDest.StretchBlt(
0, 0, 150, 150,
&dcSource, 0, 0, bmOld.bmWidth, bmOld.bmHeight,
SRCCOPY);
dcDest.SetStretchBltMode(oldStretchMode);
dcSource.SelectObject(pSourceOld);
dcDest.SelectObject(pDestold);
bResult = TRUE;
}
catch(CResourceException* /*e*/)
{
}
return bResult;
}
Well even if the code works there is some cleaniing up to do.
RAII is one of the idiom you realy need when you are working in MFC!
if( !pNewBitmap->CreateCompatibleBitmap(&dcDest, newWidth, newHeight) )
{
return FALSE;
}
When you return FALSE or there is an exception you haven't called
cSource.SelectObject(pSourceOld);
dcDest.SelectObject(pDestold);
to cleanup before you left the function.
Create a small helper class to cleanup all the time, you don't have to worry about return or throw statements.
class SelectObjectAndCleanUp
{
CDC& deviceContext;
CBitmap *const oldSource;
public:
SelectObjectCleanUp( CDC& deviceContext, CBitmap* source )
: deviceContext(deviceContext),
oldSource( deviceContext.SelectObject(source) ) {
}
~SelectObjectCleanUp() {
deviceContext.SelectObject(oldSource)
}
};
// use of the helper
SelectObjectCleanUp sourceSelectionAndCleanup(dcSource, pBitmap );
SelectObjectCleanUp destionationSelectionAndCleanup(dcDest, pNewBitmap );
I'm not too familiar with C++, but are you selecting the new bitmap into your new DC before it's created? Also, when you call CreateCompatibleBitmap, I think you want to use your screen DC (the one you used to create the destination DC), not your compatible memory DC. So, get the screen DC with GetDC, and pass that into both CreateCompatibleDC AND CreateCompatibleBitmap.
There is a great free C++ image library called CxImage that is open source under the zlib license.
No need to reinvent the wheel.
Download http://www.gdiwatch.com/, it may show you where the error is (it can be made to work with vs 2008, too - just copy the registry keys manually from the vs2005 to vs2008 directories)
Have you tried doing the CreateCompatibleBitmap() before the SelectObject() call?
Does GetBitmap() of the new bitmap return the correct size, i.e. is the new bitmap valid?
The rest seem ok, it should work like this I believe. Does it work with other StretchBltModes?
Thanks to all of the guys who peeped and suggested solutions, any how after some debugging i found the problem. Here is the solution!!!
CBitmap *SrcBmp;
HBITMAP hBmp;
hBmp= (HBITMAP)LoadImage( NULL, L"c:\\source.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE );
SrcBmp = CBitmap::FromHandle(hBmp);
BITMAP BmpInfo;
SrcBmp->GetBitmap(&BmpInfo);
CDC SrcDC;
SrcDC.CreateCompatibleDC(NULL);
CBitmap DestBmp;
DestBmp.CreateCompatibleBitmap(&SrcDC,150,150);
CDC DestDC;
DestDC.CreateCompatibleDC(NULL);
CBitmap *pOldBmp1 = SrcDC.SelectObject(SrcBmp);
CBitmap *pOldBmp2 = DestDC.SelectObject(&DestBmp);
DestDC.StretchBlt(0,0,150,150,&SrcDC,0,0,BmpInfo.bmWidth,BmpInfo.bmHeight,SRCCOPY);
CImage image;
image.Attach(DestBmp);
image.Save(_T("C:\\test.bmp"), Gdiplus::ImageFormatBMP);
SrcDC.SelectObject(pOldBmp1);
DestDC.SelectObject(pOldBmp2);