GCI object leak - what is the solution to this line of code?

B

Bob Dankert

I have traced down a GDI object leak to the following line of code:

this.sbMain.Panels[0].Icon =
Icon.FromHandle(((Bitmap)this.imlIcons.Images[n]).GetHicon());

sbMain = StatusBar Control
imlIcons = ImageList Control
n = index

This command gets run on a timer every minute, and each time this is run I
lose/leak 3 GDI objects. Every 167 minutes I lose another 500 objects, and
after 2 days or so of this running I quickly reach the 10,000 GDI object
limit and the process crashes. What is the best way to handle this so I do
not lose/leak these GDI objects?

Oddly, when I create a sample project using the same code I only leak 1 GDI
object. This is still one too many, but I am not sure why I leak 3 in my
main application and only 1 in my sample? (taking out this one line of code
from my main app eliminates the leak and I have verified it is only being
executed once per 60 seconds, so I am certain it is leaking 3 GDI objects)

Thanks,

Bob Dankert
 
J

Jeffrey Tan[MSFT]

Hi Bob,

Thanks for your post!

Can you confirm which version of .Net Framework you are using? Based on
your symptom, I think it maybe the same issue as the .Net bug below:
"Bug Details: System.Drawing.Icon.Dispose() does nothing"
http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=c
229c80e-d35a-4629-90ef-9fac7c0a2c99

Our .Net Winform team has released a hotfix regarding this bug, please
refer to the KB below:
"FIX: GDI Handles Leak When You Assign an Icon to a Windows Form"
http://support.microsoft.com/?id=819633

Hope this helps!

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
B

Bob Dankert

Jeffrey,

The framework is .Net 1.1 and I am unsure which service pack is installed
with this. Has this been resolved for .Net 2.0?

Thanks for the information!

Bob
 
J

Jeffrey Tan[MSFT]

Hi Bob,

Thanks for your feedback!

I am not sure about it yet. Based on that bug link, our product team have
fixed it in VS2005:
http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=c
229c80e-d35a-4629-90ef-9fac7c0a2c99

Thanks!

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
C

Claes Bergefall

The docs for .NET 2.0 has this little interesting remark for the
Icon.FromHandle method:
"When using this method you must dispose of the resulting icon using the
DestroyIcon method in the Win32 API to ensure the resources are released."

Dispose will only call DestroyIcon when it owns the handle and it doesn't
own it when you use FromHandle.

/claes

Bob Dankert said:
Jeffrey,

The framework is .Net 1.1 and I am unsure which service pack is installed
with this. Has this been resolved for .Net 2.0?

Thanks for the information!

Bob

"Jeffrey Tan[MSFT]" said:
Hi Bob,

Thanks for your post!

Can you confirm which version of .Net Framework you are using? Based on
your symptom, I think it maybe the same issue as the .Net bug below:
"Bug Details: System.Drawing.Icon.Dispose() does nothing"
http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=c
229c80e-d35a-4629-90ef-9fac7c0a2c99

Our .Net Winform team has released a hotfix regarding this bug, please
refer to the KB below:
"FIX: GDI Handles Leak When You Assign an Icon to a Windows Form"
http://support.microsoft.com/?id=819633

Hope this helps!

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no
rights.
 
J

Jeffrey Tan[MSFT]

Hi Claes ,
Dispose will only call DestroyIcon when it owns the handle and it doesn't
own it when you use FromHandle.
I am not sure how do you get this conclusion. With Reflector, we got the
following snippet:

private void Dispose(bool disposing)
{
if (this.handle != IntPtr.Zero)
{
this.DestroyHandle();
}
}

public static Icon FromHandle(IntPtr handle)
{
IntSecurity.ObjectFromWin32Handle.Demand();
return new Icon(handle);
}

internal Icon(IntPtr handle)
{
this.iconSize = Size.Empty;
this.handle = IntPtr.Zero;
this.ownHandle = true;
if (handle == IntPtr.Zero)
{
throw new ArgumentException(SR.GetString("InvalidGDIHandle",
new object[] { typeof(Icon).Name }));
}
this.handle = handle;
this.ownHandle = false;
}
As we can see, Icon.FromHandle internally invokes Icon constructor. The
constructor really assigns the handle to the this.handle field. So I think
DestroyHandle will be called by Dispose method without any problem. Anyway,
I think Bob can ensure this by setting a breakpoint in VS2005, then
explicitly call Dispose method.

Thanks!

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
C

Claes Bergefall

Well, you forgot to look what happens inside DestroyHandle:

internal void DestroyHandle()
{
if (this.ownHandle) <--- Note
{
SafeNativeMethods.DestroyIcon(new HandleRef(this, this.handle));
this.handle = IntPtr.Zero;
}
}

Now, if you look again at the very last row in the constructor code you
posted below you'll see this:
this.ownHandle = false;

So my conclusion is valid.

/claes

"Jeffrey Tan[MSFT]" said:
Hi Claes ,
Dispose will only call DestroyIcon when it owns the handle and it doesn't
own it when you use FromHandle.
I am not sure how do you get this conclusion. With Reflector, we got the
following snippet:

private void Dispose(bool disposing)
{
if (this.handle != IntPtr.Zero)
{
this.DestroyHandle();
}
}

public static Icon FromHandle(IntPtr handle)
{
IntSecurity.ObjectFromWin32Handle.Demand();
return new Icon(handle);
}

internal Icon(IntPtr handle)
{
this.iconSize = Size.Empty;
this.handle = IntPtr.Zero;
this.ownHandle = true;
if (handle == IntPtr.Zero)
{
throw new ArgumentException(SR.GetString("InvalidGDIHandle",
new object[] { typeof(Icon).Name }));
}
this.handle = handle;
this.ownHandle = false;
}
As we can see, Icon.FromHandle internally invokes Icon constructor. The
constructor really assigns the handle to the this.handle field. So I think
DestroyHandle will be called by Dispose method without any problem.
Anyway,
I think Bob can ensure this by setting a breakpoint in VS2005, then
explicitly call Dispose method.

Thanks!

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no
rights.
 
J

Jeffrey Tan[MSFT]

Hi Claes ,

Cool! It seems I really missed some point. :-(

Anyway, thanks for your sharing with the community!

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top