Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a memory leak possible with LoadIcon()?

Tags:

delphi

I'm using this code to animate a tray icon in a thread (icon1 and icon2 are in a .res file):

while AnimationPending do
begin
    TrayIcon.Icon.Handle := LoadIcon(hInstance,'icon1');
    Sleep(300);
    TrayIcon.Icon.Handle := LoadIcon(hInstance,'icon2');
    Sleep(300);
end;

I have the fear that it may create a memory leak if I do it in a loop because icon1/2 are loaded all over again.

Does the code create a memory leak, or is it safe to use in a loop?

like image 563
Casady Avatar asked Mar 21 '13 19:03

Casady


1 Answers

You are calling LoadIcon. That returns what are known as shared icons. This is explained in the documentation for DestroyIcon. One of the consequences of being a shared icon is that you do not need to call DestroyIcon.

It is only necessary to call DestroyIcon for icons and cursors created with the following functions: CreateIconFromResourceEx (if called without the LR_SHARED flag), CreateIconIndirect, and CopyIcon. Do not use this function to destroy a shared icon. A shared icon is valid as long as the module from which it was loaded remains in memory. The following functions obtain a shared icon.

  • LoadIcon
  • LoadImage (if you use the LR_SHARED flag)
  • CopyImage (if you use the LR_COPYRETURNORG flag and the hImage parameter is a shared icon)
  • CreateIconFromResource
  • CreateIconFromResourceEx (if you use the LR_SHARED flag)

So, how does this relate to your code? Well, when you write

TrayIcon.Icon.Handle := LoadIcon(hInstance,'icon1');

you are assigning to the Handle property of a TIcon object. If that TIcon object already contains an icon, then that icon will be destroyed before being replaced by the new icon. That's because TIcon has ownership of its icon handles. All this means that the line of code above results in a call to DestroyIcon for a shared icon. This is what MSDN tells you not to do, but in fact it turns out to be benign. It's nothing to worry about.

Now, even if you were using a function that returns non-shared icons, e.g. CreateIconIndirect then your code would not leak icon handles. That's because the TIcon class takes on ownership of the icon handle.

But since you are using shared icons, it's not even possible to leak those handles. Objects that cannot be destroyed, cannot be leaked!

Some more points:

  1. I personally would not call LoadIcon over and over like that. I would call it twice at program startup and remember the shared icon handles. Then I would use those handles to assign to TrayIcon.Icon.Handle.
  2. When you call LoadIcon you do not have a lot of control over the size of the icon returned. I think that it's possible that you will get a large icon rather than a small icon. And that will need to be scaled to the small icon size before display. When creating notification area icons you should make sure that they are SM_CXSMICON by SM_CYSMICON sized.
like image 166
David Heffernan Avatar answered Oct 15 '22 04:10

David Heffernan