Can changing ImageList used by TVS_CHECKBOXES cause resource leak? - winapi

Win32:
With a Tree Control created and the style changed to TVS_CHECKBOXES and then the ImageList for the TVSIL_STATE changed to a custom ImageList, do you need to delete the returned prior ImageList or is it a shared resource and should not be.
MFC:
Since there is an object hierarchy, in this case you don't know if the CImageList is replacing a one provided by the system or from one of the parent classes. In that case what is the proper handling? For ImageLists, can you CImageList::Attach(), CTreeCtrl::SetImageList(), CImageList::Detach() then CTreeCtrl::OnDestroy() go ahead and CImageList *pil=CTreeCtrl::SetImageList(NULL, TVSIL_STATE) and then pil->DeleteImageList(), but then what about the object, are we supposed to delete pil instead? Or are we always required to setup a member variable that is the image list and just CTreeCtrl::SetImageList() to change it then OnDestroy() put back the old one or just set it to NULL?

Can changing ImageList used by TVS_CHECKBOXES cause resource leak?
Yes. From Tree-View Control Window Styles:
Destroying the tree-view control does not destroy the check box state
image list. You must destroy it explicitly. Get the handle to the
state image list by sending the tree-view control a TVM_GETIMAGELIST
message. Then destroy the image list with ImageList_Destroy.
Bonus "Old New Thing" link: Beware of the leaked image list when using the TVS_CHECKBOXES style
Win32 Solution
We can take advantage of the fact that TVM_SETIMAGELIST (wrapped by TreeView_SetImageList()) returns the handle to the previous image list.
HIMAGELIST hNewImageList = ImageList_Create(/* insert arguments */);
HIMAGELIST hOldImageList = TreeView_SetImageList( hwndTreeView, hNewImageList, TVSIL_STATE );
if( hOldImageList ) // a good habit, to check if handle is not NULL
{
ImageList_Destroy( hOldImageList );
hOldImageList = NULL;
}
After the tree control has been destroyed, you also have to ImageList_Destroy the new image list.
MFC Solution
MFC is not that much different, i. e. it doesn't automatically clean up the image list created by the TVS_CHECKBOXES style.
Create a member variable CImageList m_newImageList; in the declaration of the class that hosts the tree control (e. g. a CDialog derived class). This makes sure that the new image lists lifetime does not end before that of the tree control window and automatically destroys the image list through its destructor.
m_newImageList.Create(/* insert arguments */);
CImageList* pOldImageList = m_treeCtrl.SetImageList( &m_newImageList, TVSIL_STATE );
if( pOldImageList )
{
pOldImageList->DeleteImageList();
pOldImageList = nullptr;
}
CTreeCtrl::SetImageList() returns a pointer to a temporary object (via CImageList::FromHandle()) that does not own the handle it wraps. You have to DeleteImageList() to avoid the resource leak, but never delete on the pointer returned by SetImageList. MFC automatically cleans up temporary objects during idle processing (in CWinApp::OnIdle()).
Further reading:
TN003: Mapping of Windows Handles to Objects.
What is the lifetime of a CWnd obtained from CWnd::FromHandle?
(replace CWnd by CImageList, the FromHandle() methods of MFC classes all work in the same way).

Related

TreeView Control will leak normal ImageList but ListView control doesn't?

I was using task manager to watch my dynamically created controls and found each time I'd create a TreeView with an ImageList, the GDI objects count would increase by 4 each time I destroyed the tree and created again. However, a ListView never had a problem.
I know of the TVS_CHECKBOXES issues with the state images and was already destroying of the state imagelist, but I then implemented:
ImageList_Destroy(TreeView_SetImageList(GetHandle(), nullptr, TVSIL_NORMAL));
and now the resource leak is gone.
So far it looks like you have to manually clean up images in WM_DESTROY for the following:
Button_SetImageList() - Have to set it to switch it to `BUTTON_IMAGELIST.himl=BCCL_NOGLYPH` to clear it.
TreeView_SetImageList(LVILS_STATE) - if you set it or used `TVS_CHECKBOXES`
TreeView_SetImageList(LVILS_NORMAL) - if you set it
BM_SETIMAGE and STM_SETIMAGE - destroy your own but also set to NULL and destroy returned handle to get rid of potential hidden bitmap handle if different handle than your own.
But a ListView is different, is that by design or should I just go ahead in WM_DESTROY with something like:
ImageList_Destroy(ListView_SetImageList(GetHandle(), nullptr, LVSIL_STATE));
ImageList_Destroy(ListView_SetImageList(GetHandle(), nullptr, LVSIL_SMALL));
ImageList_Destroy(ListView_SetImageList(GetHandle(), nullptr, LVSIL_NORMAL));
Note that using WM_NCDESTROY is too late for TreeViews.
The two controls are inconsistent in this regard.
The tree control does not take ownership of the imagelist you give it, so you remain responsible for freeing it.
The listview control does take ownership, unless you set the LVS_SHAREIMAGELISTS window style.
Note that the tree control also has a related quirk; if you set the TVS_CHECKBOXES style, you are responsible for freeing the state image list even though you didn't create it.
What you are seeing is documented behavior.
For a TreeView:
TVM_SETIMAGELIST message
TreeView_SetImageList macro
The tree-view control will not destroy the image list specified with this message. Your application must destroy the image list when it is no longer needed.
As well as:
Tree-View Control Window Styles
Constant
Description
TVS_CHECKBOXES
Version 4.70. Enables check boxes for items in a tree-view control. A check box is displayed only if an image is associated with the item. When set to this style, the control effectively uses DrawFrameControl to create and set a state image list containing two images. State image 1 is the unchecked box and state image 2 is the checked box. Setting the state image to zero removes the check box altogether. For more information, see Working with state image indexes.Version 5.80. Displays a check box even if no image is associated with the item. Once a tree-view control is created with this style, the style cannot be removed. Instead, you must destroy the control and create a new one in its place. Destroying the tree-view control does not destroy the check box state image list. You must destroy it explicitly. Get the handle to the state image list by sending the tree-view control a TVM_GETIMAGELIST message. Then destroy the image list with ImageList_Destroy.If you want to use this style, you must set the TVS_CHECKBOXES style with SetWindowLong after you create the treeview control, and before you populate the tree. Otherwise, the checkboxes might appear unchecked, depending on timing issues.
Compared to a ListView:
LVM_SETIMAGELIST message
ListView_SetImageList macro
The current image list will be destroyed when the list-view control is destroyed unless the LVS_SHAREIMAGELISTS style is set. If you use this message to replace one image list with another, your application must explicitly destroy all image lists other than the current one.

Do I need to call SelectObject() to restore a object retreived with GetStockObject()?

I'm using MFC...
When I create a new brush, I know I need to restore the old bush using SelectObject:
CBrush brushFill;
brushFill.CreateSolidBrush(colorFill);
CBrush *oldBrush = pDC->SelectObject(&brushFill);
// Draw something here.
pDC->SelectObject(oldBrush);
However, if I get the brush using GetStockObject(), do I need to to restore it, too?
CBrush *oldBrush = (CBrush *)pDC->SelectObject(GetStockObject(HOLLOW_BRUSH));
// Draw something here.
pDC->SelectObject(oldBrush);
I ask, because that code occasionally crashes. I'm unsure if that's due to me not supposed to save/restore stock items, or my typecast to a CBrush*. If the latter, I'm thinking I should save/restore an HGDIOBJ handle:
HGDIOBJ oldBrush = pDC->SelectObject(GetStockObject(HOLLOW_BRUSH));
// Draw something here.
pDC->SelectObject(oldBrush);
If the former, I wouldn't save/restore the previous object.
You should always 'bracket' any operations on a device context with the SaveDC() and RestoreDC() member functions of the CDC object:
int DCsave = pDC->SaveDC(); // Saves all (or most) settings and object selections
// ... Do your drawing operations, etc.
pDC->RestoreDC(DCsave); // Restores the saved state and all selected objects
Also, just for info, you may find the SelectStockObject() member a bit easier to use:
pDC->SelectStockObject(HOLLOW_BRUSH);
Note (from the comment made by IInspectable): Although deselection of the GDI 'Stock Objects' may seem unnecessary (after all, there will be several of these selected by default), other code may be relying on having the previous object (brush, in your case) selected; failure to restore this selection can thus cause that code to fail in ways that are near impossible to track down.

How to get the parent structure as Spy++ gives it?

In our code, a C++ class that is derived from CMDIChildWnd is instantiated and its Create() function is called, which is actually CMDIChildWnd::Create(). The 5th parameter pParentWnd is being set to a CMDIFrameWnd object. This in turn (within the MFC code) sets up a CREATESTRUCT object, sets it's hwndParent to that parameter and then calls PreCreateWindow() with that object passed as one of the parameters.
However, when going up the parent window list using CWnd::GetParent() (original) GetAncestor(hwnd, GA_PARENT) (just a try, since the original didn't work and this code is 20+ years old) and even GetWindow(hwnd, GW_OWNER) just to see if it was setting the window's owner for some reason. In each case, I do not see the handle which is associated with the CMDIFrameWnd object. Instead, the parent shown is the desktop, and owner is NULL.
Using Spy++, it shows what I was expecting. How would I get the layout given by Spy++ programmatically?
In MDI interface CMDIChildWnd is usually passed to CMultiDocTemplate, then we create a new window through CDocManager::OnFileNew, that in turn calls CFrameWnd::LoadFrame which passes NULL for child window's parent (at least in VS 2017)
We usually don't call mdi_child->Create directly, unless we initialize everything else manually. Perhaps you mean the base class CMDIChildWnd::Create is called, or you are able to override it.
In child window,
::GetAncestor(m_hWnd, GA_ROOT) or ::GetAncestor(m_hWnd, GA_ROOTOWNER)
Should return a reference to the main frame, otherwise use AfxGetMainWnd()

Resizing Render Target Direct2D after WM_SIZE

Quick question guys... I am currently working with Directx3D and 2D and I was wondering if I have to recreate the render target when the Windows is resized or does Direct2D automatically detects this, since it's bound to the DXGISurface(back-buffer of the swapchain) when I created it.
Here is the code that I used to bind the render targets together:
ComPtr<IDXGISurface1> dxgibackBuffer;
hr = m_pDxSwapchain->GetBuffer(0, IID_PPV_ARGS(&dxgibackBuffer));
D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
96.0f,
96.0f);
hr = m_pD2DContext->CreateBitmapFromDxgiSurface(dxgibackBuffer.Get(), &bitmapProperties, &m_pD2DTargetBitmap);
// last step
m_pD2DContext->SetTarget(m_pD2DTargetBitmap.Get());
See Care_and_Feeding_of_the_Swap_Chain and Handling_Window_Resizing
Quote1:
Naturally, the application's best route is to respond to WM_SIZE, and call IDXGISwapChain::ResizeBuffers, passing the size contained in the message's parameters.
Quote2:
Before you call ResizeBuffers, you must release all outstanding references to the swap chain's buffers. The object that typically holds a reference to a swap chain's buffer is a render-target-view.

DestroyIcon after SHGetFileInfo?

I'm using SHGetFileInfo to get the icon of specific file type. The MSDN says about the SHFILEINFO:
hIcon
Type: HICON
A handle to the icon that represents the file. You are responsible
for destroying this handle with DestroyIcon when you no longer
need it.
To get the icon from HIcon, I use Icon.FromHandle. Again, MSDN says:
Remarks
When using this method, you must dispose of the original icon
by using the DestroyIcon method in the Win32 API to ensure
that the resources are released.
It is even more confusing as SGHFI_ICON description contains the following information:
SHGFI_ICON (0x000000100)
Retrieve the handle to the icon that represents the file and the
index of the icon within the system image list. The handle is
copied to the hIcon member of the structure specified by psfi,
and the index is copied to the iIcon member.
From this description, it seems like the handle is kept by the OS and I shouldn't actually destroy it.
My question is then: if, and if so, when should I dispose of the icon handle?
Immediately after Icon.FromHandle() ?
When I no longer need the icon created from Icon.FromHandle() ? (in such case I guess that I'd rather copy the icon, release the original and return the copy to avoid handle leaks)
Never (it will be done automatically somehow? Many examples of SHGetFileInfo - even on SO - does not include any code releasing the icon handle)
The system doesn't keep icon handles, it keeps image lists. When you use the SHGFI_ICON flag, SHGetFileInfo creates an icon by calling ImageList_GetIcon against the system image list. You can do this yourself by passing the SHGFI_SYSICONINDEX flag instead to retrieve just the icon index.
The icon should be destroyed when you're done with it.
No, you really do have to call DestroyIcon yourself. The Icon.FromHandle() method is pretty useless to help you with that, it still requires you to call DestroyIcon. You've got to implement your own garbage collector code to know that the icon is no longer in use so it is safe to pinvoke DestroyIcon.
This is cruel and unusual punishment. The Icon class does in fact have a constructor that can create an Icon object from a handle and owns the handle, automatically calling DestroyIcon when the Icon object is disposed or finalized. They however made it inaccessible for unphantomable reasons, major bummer. Reflection to the rescue, you can bypass this silly restriction and still use the constructor, find the code in this answer.

Resources