In MFC I'm loading a .PNG image and thereafter, I'm displaying it with a static control as below:
CImage Img;
Img.Load(_T(abc.png"));
CBitMap Bmp;
Bmp.Attach(Img.Detach());
CStatic dispImg;
dispImg.SetBitmap(Bmp);
In this way I've several images which I'm displaying with the static control repetitively
which is causing significant memory leak within my application. I found several clues by googling, but I couldn't decide the best option for releasing memory. Could anybody please guide me with the best approach.
This code is incomplete. Using a CStatic in this way will never work. It isn't created.
If you realy want help that macthes your question give a more detailed description.
When you use CStatic::SetBitmap ypu must free the HBITMAP handle that is returned by the function. Just read the docs. So if you use this samne CStatic in a Loop and always assign a new Bitmap to the CStatic this may cause a leak.
As xMRi suggested I'm freeing HBITMAP handle returned by CStatic::SetBitmap. In my test app there is a CStatic control member m_pic, a method ChangePic which sets a Bitmap and a button control for changing the image based on a value of a boolean variable SetBlueImg. Defination of ChangePic as below:
void CPNGTestDlg::ChangePic(Cstring img)
{
CImage Img;
Img.Load(img);
CBitmap Bmp;
Bmp.Attach(Img.Detach());
HBITMAP hBmp=m_pic.SEtBitmap(Bmp);
if(hBmp!=NULL) DeleteObject(hBmp);
}
Click event of the button is defined as below:
void CPNGTest::OnBnClickedChngPic()
{
if(SetBlueImg)
{
ChangePic(_T("Blue.png"));
SetBlueImg=false;
}
else
{
ChangePic(_T("Red.png"));
SetBlueImg=true;
}
}
Initial value of SetBlueImg=true and in OnInitDialog() I called ChangePic(_T("Red.png"));
I ran the application and continuously clicking the button to change current image but still I can see memory leaks through windows task manager. Hike of memory is by 4KB after clicking button 40/50 times or sometimes unpredictably. Please, point out where I'm missing something which is causing the leaks.
To get rid of overlapped images when replacing one image on a Static Control with another one, I initially followed the steps below:
ShowWindow(SW_HIDE)
SetBitmap()
ShowWindow(SW_SHOW)
but I didn't know whether this is a correct way to solve the image overlapping. I searched a bit by googling and found the following way:
Copying area on the parent window behind the static control before it is drawn for the first time.
Copying the image back to the parent every time the static control needs to be redrawn.
The HBITMAP object has to be deleted when the dialog is destroyed.
void CMFCAppDlg::OnDestroy()
{
CDialogEx::OnDestroy();
DeleteObject(m_pic.GetBitmap());
}
Related
I have an MFC based point of sale application that will be presenting a series of images on a customer facing display as the operator is ringing up items.
To handle the presenting of images, I create a window that contains a CStatic control which is displayed on the customer facing monitor. I am using the CImageclass to load the image from a disk file and then setting the image into the CStatic control.
The prototype with hard coded image names looks like:
CImage CImg;
HRESULT hr;
CONST TCHAR *px;
// determine the image to display based on the current OEP group number being processed.
switch (m_aszCurrentOEPGroup) {
case 16:
hr = CImg.Load( px = PifGetFilePath (_T("PlatedSandwiches.png"), "b", NULL) );
break;
case 20:
hr = CImg.Load( px = PifGetFilePath (_T("modem_after20.png"), "b", NULL) );
break;
default:
hr = CImg.Load( px = PifGetFilePath (_T("modem_after00.png"), "b", NULL) );
break;
}
if( m_OEPCustDisplayImage.GetBitmap( ) == NULL )
m_OEPCustDisplayImage.SetBitmap( HBITMAP(CImg) );
The above code is in a class derived from CWnd and contains the member variable m_OEPCustDisplayImage which is defined as CStatic m_OEPCustDisplayImage;. And looking at this, I really do need to check the result variable hr as to whether the Load() worked. And it also looks like Load() can throw an exception so I should consider that as well.
The above source code is in a class member function that is called to pop up the customer facing image display window, load the required image from a file in a specific folder, and then set the image into the CStatic to display the image.
By pop up the display window, what the code actually does is to do a ShowWindow(SW_SHOW) when displaying the image and a ShowWindow(SW_HIDE) when popping down or removing the displayed image.
This source code is displaying the images as expected.
However I am concerned about resource lifetimes especially of the images that have been loaded. I do not want to create a memory leak.
Since the customer facing display window is created once and then exists until the application exits, the destructor for the window is not a mechanism for resource life cycle management.
The CImage is used to load the image from the file and since it is a local object, when the function returns, it's destructor will be called for cleanup.
I am trying to understand the actual lifecycle of an image and what is happening with the resources being used during the following steps:
the CImage is defined
the image is loaded from a file into the CImage using load()
the image is put into the CStatic using the SetBitmap()
the CImage object goes out of scope and its destructor called
the image in the CStatic is replaced by a new image or with NULL
Some of the lifecycle questions that come to mind are:
when the SetBitmap() is used to put the image into the CStatic is a new copy of the image made?
when the CImage destructor triggers when the object goes out of scope, is the image resources cleaned up? (I would expect so)
what do I need to do with the image in the CStatic when replacing it with a new image?
Currently in the customer facing window pop down code I am just setting the image in the CStatic to NULL with m_OEPCustDisplayImage.SetBitmap(NULL); however it would seem to me that this would be a resource leak. What happens to the image that had been displayed in the CStatic?
The behavior I am seeing of the application is that the requested image is being loaded into and displayed with the CStatic control which is resized to be the same size as the CWnd containing it using ::SetWindowPos(). The window displays and undisplays as expected. The images change as expected.
Since the image is displayed in the CStatic until the next operator action causes the displayed window to undisplay, it would seem that the SetBitmap() method of the CStatic made its own copy of the image.
Do I need to do something like the following as a part of the customer facing window undisplay and pop down?
CGdiObject myTempObj(m_OEPCustDisplayImage.SetBitmap(NULL));
and then let this object go out of scope so as to cleanup any resources?
I have looked at the Output window of Visual Studio after exiting the application and do not see any indication that these images are causing a resource leak from complaints of the debug Visual Studio C++ Runtime however I am not sure that a resource leak of this type, should it exist, would be identified by the C++ Runtime.
Addendum A: Source Changes to Correct Resource Leaks, retest
While looking for whether the images were not being released properly, I was unable to determine this due to another source of resource leaks the result of which I could see in Windows Task Manager that the GDI Object Count was going up however I was unsure as to what was due to the images and what was due to the other resource leak, button pointers not being properly disposed of.
I could see in the Output window of Visual Studio as the application terminated the list of button pointer leaks as well as their locations. However I did not see any indication of the image resources leaking.
I tracked down the reason for the button pointers resource leak and corrected that.
I also modified the source code for the images as follows.
In the code that handles the undisplay or pop down of the window with the CStatic displaying the image I have the following cleanup source code which moves ownership of the image from the CStatic to a CGdiObject which then goes out of scope deleting the image as it does so:
CGdiObject myObj;
myObj.Attach(m_OEPCustDisplayImage.SetBitmap(NULL));
Then in the code that loads first the CImage object with an image from a file and then loads the image into the CStatic object I made the following change using the Detach() method of CImage with SetBitmap() of CStatic to transfer ownership of the image to the CStatic:
if( ! FAILED(hr)) {
if (m_OEPCustDisplayImage.GetBitmap( ) == NULL )
m_OEPCustDisplayImage.SetBitmap( CImg.Detach() );
else {
CGdiObject myObj;
myObj.Attach(m_OEPCustDisplayImage.SetBitmap(CImg.Detach()));
}
}
After doing these two efforts, I was then able to monitor the GDI Object count using the Windows Task Manager while exercising the application in Debug mode. I could see the GDI Object count increase as the windows with images were displayed and then after they undisplayed and popped down, the count went back to the count before the functionality was triggered.
Setting a breakpoint so that the code to transfer the ownership of the image to the CGdiObject with the SetBitmap(NULL) was replaced with just the call to SetBitmap(NULL) resulted in the GDI Object count increasing as expected.
I then recompiled the corrected source as a Release build and exercised the application in this area of functionality while watching the Windows Task Manager display of the GDI Object count. The count behavior was to increase as the windows with the images were displayed and to decrease back to a baseline value when the images were undisplayed.
Above code is using a png file which can have transparency. This is a special case where SetBitmap ends up creating a different bitmap handle. The image will remain even after the original bitmap handle is destroyed. This method will otherwise fail, for example for 24-bit bitmap.
SetBitmap uses STM_SETIMAGE api described with the following remarks:
With Windows XP, if the bitmap passed in the STM_SETIMAGE message
contains pixels with nonzero alpha, the static control takes a copy of
the bitmap. This copied bitmap is returned by the next STM_SETIMAGE
message. The client code may independently track the bitmaps passed to
the static control, but if it does not check and release the bitmaps
returned from STM_SETIMAGE messages, the bitmaps are leaked.
You are calling SetBitmap only once, based on testing for GetBitmap. You are leaking 1 resource handle (allowed limit is 10,000) This might be difficult to detect, but it's there. It can run in to problem if you open and close the dialog many times, within the same process.
It is preferable to use CImage::Detach instead of HBITMAP(cimage_object)
The example shown below will not have resource leaks and works with other images. It uses an additional variable bitmap_cleanup to clean the bitmap handle at the end.
class CMyDialog : public CDialogEx
{
CStatic m_OEPCustDisplayImage;
CBitmap bitmap_cleanup;
...
};
BOOL CMyDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
CImage img;
HRESULT hr = img.Load(L"test.png");
if(SUCCEEDED(hr))
{
HBITMAP hbitmap = img.Detach();
//CImage is no longer responsible for cleanup
HBITMAP holdbmp = m_OEPCustDisplayImage.SetBitmap(hbitmap);
//we have to cleanup previous handle if any
if(holdbmp)
DeleteObject(holdbmp);
//pass the handle to bitmap_cleanup, to cleanup on exit
bitmap_cleanup.Attach(hbitmap);
}
return TRUE;
}
INTRODUCTION AND RELEVANT INFORMATION:
I have implemented complex painting of the main window’s background and its child static controls.
The picture below shows how it looks.
Static controls have SS_NOTIFY style, which is important to mention, as certain things happen when user clicks on them.
At this point, actions activated when clicking on them, are not relevant.
Both main window, and static controls, have gradient backgrounds, which were made through usage of GradientFill(...) API.
Top banner of the main window is created with gray brush, and the grid lines were created with LineTo(...) and MoveTo(...) API.
Map on the orange static control, and the top left logo are EMF files, top right logo is PNG file, and other pictures are bitmaps.
Orange static control has 4 child static controls which are owner drawn and also have SS_NOTIFY style.
It was the only way I could have thought of, which enabled me to draw the control the way it was asked of me ( if I can improve on this, please suggest it, I will accept any reasonable suggestion ).
In order to paint the orange static control, I have decided to paint its background in WM_CTLCOLORSTATIC handler, and to owner draw child static controls in a subclass procedure.
Notifications received from child static controls are also handled in the orange static controls subclass procedure, since I didn’t know how to forward them to the parent window, but are omitted since they are also irrelevant at this moment.
I have decided to provide link to the demo project, instead of making this post quite lengthy with code snippets.
I have tried to submit demo application as small and simple as it is possible.
I did not skimp on the commentaries, so I believe everything is well covered and explained in source code.
If there are still questions please leave a comment and I will reply as soon as possible ( usually immediately, or in the same day, at least ).
Here is the link to the demo project:http://www.filedropper.com/geotermistgrafika_1
Important update:
/==========================================================/
Text bellow in square brackets was the original part of the question, but is now omitted since the project had memory leaks.The above link links to an improved version.
[ Updated in response to member xMRi's comment: This link should be fine: http://www.filedropper.com/geotermistgrafika ]
/==========================================================/
I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.
One note: since Express edition of VS has no resource editor, resource file and resource header were created using ResEdit from here: http://www.resedit.net/.
PROBLEM:
When I resize my window, static controls slightly flicker.
MY EFFORTS TO SOLVE PROBLEM:
I believe that my code has no memory leaks-therefore I doubt this is the problem, but being inexperienced, I would highly appreciate if my assumption can be somehow confirmed.
I think that I have properly handled WM_ERASEBKGND, and I have excluded styles CS_VREDRAW and CS_HREDRAW from my window class-therefore flickering should not be caused because of this.
I have forgot to mention, that my window has WS_CLIPCHILDREN style, so I am mentioning that now, in response to the comment bellow made by member Roger Rowland.
I have implemented double buffering for both handlers, in order to avoid flickering.
QUESTIONS:
How can I modify code in demo project to get rid of flickering?
I need advice on how to optimize both WM_PAINT and WM_CTLCOLORSTATIC handlers, so my painting code gets more efficient and faster.
A small note for second question:
I was thinking to improve my code by drawing the entire picture on the main window’s background, and to put transparent static controls on top of the part of the picture that corresponds that static controls background.
That way, I would only return NULL_BRUSH in my WM_CTLCOLORSTATIC handler, and do all the work in the WM_PAINT.
Am I on the right track with this idea? Could this work ?
Thank you.
Regards.
Firstly, your App is leaky as hell. Haven't looked for leaks, but most of them should be in WM_CTLCOLORSTATIC as you forget to delete HBITMAP's(use this neat freeware http://www.nirsoft.net/utils/gdi_handles.html to look for gdi leaks).
Secondly, your code is way to big. I noticed that you didn't use functions, maybe because you don't know what they are capable of. For example I would use:
void DrawBackground(HDC &hDC, SOMEINFOSTRUCT GradientInfo, LPCTSTR Text);
to simplify your code a lot.
Anyway enough of lecturing, let's go back to your problem. In WM_CTLCOLORSTATIC you must return brush, you want to paint background with. What you're doing now is painting background manually using Bitblt(), then return NULL brush and program paints it on your already painted background. Instead of painting it yourself, let the brush do the job.
Simply instead of the last Bitblt() use CreatePatternBrush(), but then you need to take care of this Brush and here is what you should do:
HBRUSH TempBrush = NULL; //Create global brush
//Some Code....
case WM_CTLCOLORSTATIC:
{
if (TempBrush != NULL)
{
DeleteObject(TempBrush);
TempBrush = NULL;
}
//Let's skip to the end....
GradientFill( MemDC, vertex3, 3, &gTriangle, 1,
GRADIENT_FILL_TRIANGLE );
TempBrush = CreatePatternBrush(bmp);// these 3 line should be at the
//end of every if
DeleteDC(MemDC); // or put them once outside if's
DeleteObject(bmp); // also if you delete HDC first, you don't need to
//unselect hbitmap
}
return (LRESULT)TempBrush;
}
break;
case WM_CLOSE:
{
if (TempBrush != NULL)
{
DeleteObject(TempBrush);
TempBrush = NULL;
}
//.......
I have a mode less dialog that I have changed the shape into a roundrect using SetWindowRgn(). I would like to draw a colored border around it using FrameRgn. Here is the code I am using
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog(); m_Brush.CreateSolidBrush(RGB(255,255,255));
CRect rcDialog;
GetClientRect(rcDialog);
// This Creates area assigned to Dialog: This goes directly below the above in OnInitDialog
m_rgnShape.CreateRoundRectRgn(rcDialog.TopLeft().x, rcDialog.TopLeft().y,rcDialog.BottomRight().x,rcDialog.BottomRight().y, rcDialog.Width()/8, rcDialog.Height()/8);
::SetWindowRgn(GetSafeHwnd(), (HRGN)m_rgnShape, TRUE);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CMyDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
CBrush brush;
brush.CreateSolidBrush(RGB(255,0,0));
dc.FrameRgn(&m_rgnShape, &brush, 2, 2);
}
Can anyone explain why the FrameRgn is not working, and maybe provide some sample code here
that will make it work.
As shown in the CWnd::SetWindowRgn documentation:
After a successful call to SetWindowRgn, the operating system owns the
region specified by the region handle hRgn. The operating system does
not make a copy of the region, so do not make any further function
calls with this region handle, and do not close this region handle.
What this basically means is that you can't then go and use the region for another purpose, and you also can't "lose" the region. As it's a member variable, this last issue isn't a problem you need to worry about. But regarding the "do not use it" part, you will notice that the FrameRgn(...) call most likely returned zero, indicating the failure when trying to draw.
What you can do is to detach the region handle from the CRgn object and use that to set the window region, then you can recreate a new one as before:
m_rgnShape.CreateRoundRectRgn(...);
HGDIOBJ hRgn = m_rgnShape.Detach();
::SetWindowRgn(GetSafeHwnd(), (HRGN)hRgn, TRUE);
m_rgnShape.CreateRoundRectRgn(...);
For a better description, have a look at this article which covers Setting a Window Region, to make it look like a cat.
Edit: Your comment mentions that now, the framed region is effectively offset by an amount. The amount is likely to be the size of the border of your window.
When you call GetClientRect, it returns the size of the client area of the window - the part you can easily draw on, and the part that is "described" by the device context when you do CPaintDC dc(this); in your OnPaint() method.
The reason for the offset is that your window has a border, which you don't normally draw on (there are ways, but we'll ignore those for now). So the device context describes an area that's offset from your window.
The simplest solution to this in your case is likely to be to modify the dialog template to specify no borders. This will of course limit resizing the window, but as you've already set a region, I'm assuming resizing isn't an option either.
I'm updating an MFC dialog with a number of buttons on it.
At present, the dialog has a Picture control covering the whole dialog providing a patterned background. On top of that, each button is a CBitmapButton using (opaque) images carefully generated to match the area of background they cover.
It would obviously be much easier if the images could be created as mostly transparent, so the background shows through automatically. However, I can't work out how to get MFC to render transparent images correctly in this case.
I understand that I might want a different class to CBitmapButton, or need to write a custom subclass; that's fine, but I don't know where to start. It would be nice to support 32-bit BMP or PNG with alpha channel, but I'd settle for the "specified colour should be transparent" type.
It may not be the best way to do it, but what I'd do is create a custom CButton derived class (assuming that you're actually using the rest of the CButton functionality), then override the DrawItem function to put your custom draw code in.
For the image itself I'd use a Bitmap GDI+ object (which will allow you to load either BMPs or PNGs with alpha channels) then use the regular DrawImage function to draw the bitmap.
If you're going to put PNGs into your resource file then you need to put them in as a "PNG" type. Make sure when you look in the resource file code that the entry looks like
IDB_PNG1 PNG "C:\temp\test.png"
and doesn't try to treat it as a BITMAP resource otherwise you'll have problems loading them.
Edit
Putting my response here so I can post code. Yes, I meant to derive a custom class from CButton, then add a Gdiplus::Bitmap member variable. Here is roughly what you'll need to do to get it to work, though I haven't checked that the code actually compiles and works, but hopefully you'll get the idea. It's not the most efficient way to do it, but if you've not done much custom drawing before then it does have the advantage of being simple!
void CMyButton::LoadImage(const int resourceID)
{
m_pBitmap = Gdiplus::Bitmap::FromResource(NULL, MAKEINTRESOURCE(resourceID));
ASSERT(m_pBitmap);
}
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);
CRect rcClient;
GetClientRect(&rcClient);
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
// If you want to do anything special when the button is pressed, do it here
// Maybe offset the rect to give the impression of the button being pressed?
rcClient.OffsetRect(1,1);
}
Graphics gr(lpDrawItemStruct->hDC);
gr.DrawImage(m_pBitmap, rcClient.left, rcClient.top);
}
The current way that I found was to make a bitmap and paint it into a picturebox,
but there it is very slow when trying to scroll the panel (the picturebox is inside a scrollable panel).
Is there a better way to achieve this functionality?
Load the bitmap only when your picture box is created, then leave it in memory until the picture box is destroyed. Loading the bitmap every time OnPaint is called will destroy performance.
Also make sure you aren't re-painting your bitmap if you receive a WM_PAINT message and nothing has changed.
That image size, 1500x2000 will be slow. How about resizing it to 25% to show a preview and if a user wishes to see the whole image give them an alert it may take some time.