Solved

"Losing" my icons - what am I missing?

Posted on 2002-05-19
16
236 Views
Last Modified: 2010-04-04
Hello everybody,

I guess this is not so hard (for 150 points), but URGENT - so the 150 points include an add-on for express delivery :-))

My application uses a system tray icon and I want (it) to be able to display a variety of icons, even some newly loaded from the web.
I have that (mostly) working and I use

newIcon := TIcon.Create;
newIcon.loadFromStream(myMemoryStreamNewlyLoadedFromHTTP);

This works fine when I first use it, but when I want to use the same icon again later on, I get the Windows-logo instead!
(wonder where that comes from?! Probably some really smart M$-default?)
Seems like some associated resource was released? I really don't know...
I store the TIcon-instances in a TStringList (using addObject)
The "handle"-property remains unchanged, but it doesn't work any more...
This does NOT happen to the icons that are embedded in my exe-file and loaded by using LoadIcon(...) - I can use those as often and as long as I want!

What am I missing?
The memory stream is freed, of course. Just for testing, I tried not freeing it, but that didn't make any difference (I hadn't expected it to make a difference, but I'm getting desparate!)

I appreciate any hints! I had the functionality working in 10 minutes and then spent more than an hour with this unexpected problem! I've also read the documentation about icons and some web-pages but couldn't find
anything about that!

Thanks in advance,
                           Ronald
0
Comment
Question by:Ronald112197
  • 7
  • 6
  • 2
  • +1
16 Comments
 
LVL 4

Expert Comment

by:nestorua
ID: 7019895
HI,
Every time you use your Icon again you load it from Stream?
Sincerely,
Nestorua.
0
 
LVL 2

Author Comment

by:Ronald112197
ID: 7019923
No, I don't.
I just load the icon once and then store it.
I do NOT want to load it from the stream every time because the stream will be HTTP-data. Of course I could keep a memory-stream to load the icon every time, but that just CAN'T be the only solution. Also, that would make it just SO much harder to keep track of the icons I have to free...

Ronald

P.S.: As I said: I do the same to some icons which I loaded from the exe-file and they work just fine and as I expected...
0
 
LVL 4

Expert Comment

by:nestorua
ID: 7019933
Try to save your Stream to the disk and load ICON from it.
Will it have the same effect?
 
0
 
LVL 2

Author Comment

by:Ronald112197
ID: 7020028
Yes - I had already tried that... even saving different icons to different files...

This is really weird: After reading the icon from the stream, the stream should not be used any more! TIcon is defined in graphics.pas - the first thing that TIcon.LoadFromStream does is copy the entire stream to its own TMemoryStream.

I kind of think this must be related to interfaces and reference counting... maybe I'm doing something wrong so Delphi/Windows thinks that the icon can be released?
Internally, TIcon holds a TIconImage which is derived from TSharedImage which does some kind of reference counting...
I must admit I'm not familiar with reference counting or even interfaces in Delphi (just in Java), so I'm at a loss here...

Ronald

0
 
LVL 33

Expert Comment

by:Slick812
ID: 7020493
I'm not sure from your description, but the other disapearing icons may be "Freed" by the system. I have used the API CopyIcon( ) function to get a copy icon from another application (or dll), so I can use that Icon even if the other app closes.

The CopyIcon function enables an application or dynamic-link library (DLL) to get its own handle to an icon owned by another module. If the other module is freed, the application icon will still be able to use the icon.

newIcon := TIcon.Create;
TempIcon := TIcon.Create;
TempIcon.loadFromStream(myMemoryStreamNewlyLoadedFromHTTP);
hIcon1 := CopyIcon(TempIcon.Handle);
newIcon.Handle := hIcon1;
TempIcon.Free;

or something to get the Icons you use into your Apps Graphic Objects memory space
0
 
LVL 20

Expert Comment

by:Madshi
ID: 7020961
If you still have the problem, please post all the relevant code here.
0
 
LVL 2

Author Comment

by:Ronald112197
ID: 7021129
Hello everybody,

yeah, too bad, I still have the problem. I woke up this morning and thought I had the solution, but it didn't work :-( (still funny, thing, the brain *g*)

Slick812: I've tried your suggestion but it didn't work. It somehow looked promising, but I don't load other application's icons - I just load a bunch of bits from the internet and create as icon. afai can see, TIcon immediately copies those bits to an internal datastructure - I don't see how they could get lost or become inaccessible...??

The relevant code seems to be this:

IF (downloader.doDownload) THEN BEGIN
     downloader.data.Position := 0;
     newIcon := TIcon.Create;
     newIcon.LoadFromStream(downloader.data);
     STI.FlashIcons[i] := newIcon;
     Settings.Icons.AddObject(str, STI.FlashIcons[i]);
     ...
a few lines down, I use newIcon to display it as the window's icon. That works fine. When I use the same icon just a little later (using the STI.FlashIcons - array), I get the Windows logo...

In another place, I load this icon:
     MessageIcon:=TIcon.Create;
     MessageIcon.Handle:=LoadIcon(hInstance, 'NOTIFYICON');
     Icons.AddObject('MESSAGE_ICON', MessageIcon);

The "Icons"-list is the same as above. THIS icon works fine. Even when I use it for flashing afterwards, it flashes between the correct MessageIcon and the Windows logo...
I figure this is somehow related to variables going out of scope (I never explicitly "free" anything here, but "newIcon" is a local variable whereas MessageIcon is a member variable - I have not any luck with using global variables for "newIcon" either...), but have no clue how reference counting works in Delphi :-((

Anyway: I took a look at graphics.pas and it looks like an icon should always create an image when one is needed from its internal "data buffer"...

Thx for any help,
                       Ronald
0
 
LVL 2

Author Comment

by:Ronald112197
ID: 7021155
Ohhhh boy, always the same story: Ask someone to "post all relevant code" and he won't post the REALLY relevant code - coz if he knew which parts are relevant, he might fix it himself *ggg*

I've noticed something else: I can use the icon just once. Doesn't have to be immediately, but just once.
In my "flashing" routine, I do
Application.Icon.Handle := myIcon.Handle;

For some reason, the application releases my handle when I assign the next icon??

As a "hot fix", I replaced it with
Application.Icon.Handle := copyIcon(myIcon.Handle);

Now it works! GREAT!!!

I'm still not "quite" satisfied with the solution because it looks like this will make hundreds of copies of icons...  (which system resources do "icons" consume other than memory?)

Why does the application do this? Why does it do it only for the "dynamically loaded" icons (from a stream) and what's the proper solution?

As I have solved the "problem" myself, I guess I could / should still award points for answering these questions and/or some insightful comments :-)))

Thanks a lot,
                 Ronald
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 20

Expert Comment

by:Madshi
ID: 7021159
Hmmmmm... Still not enough code for me. I can't really follow what's happening. I need at least all the type definitions, better yet also all the code where you touch STI.
0
 
LVL 2

Author Comment

by:Ronald112197
ID: 7021165
I guess our last posts "crossed", right?
If you still need more info than I posted in my last comment, please let me know (again).

Ronald
0
 
LVL 20

Accepted Solution

by:
Madshi earned 150 total points
ID: 7021170
Ah, you were faster...   :-)

Well, if you do this "Application.Icon.Handle := xxx" assignment, the old icon handle is freed, that's right. Try to assign the whole TIcon structure instead of only the icon:

Application.Icon := myIcon;

This way Delphi (theoretically, at least in D6) does reference counting for the icon. So it should theoretically work.

Regards, Madshi.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 7021174
>> which system resources do "icons" consume other than memory?

GDI resources, and they're quite precious, especially in win9x. So you really should be careful with this stuff. However, as long as Application.Icon does free your copied handle, everything is fine. Better yet would be, if you would not always copy and free the icon over and over again.
0
 
LVL 2

Author Comment

by:Ronald112197
ID: 7021197
Yep, That works! Great! :-)) (Application.Icon := myIcon)

Still:
Why does the application do this? Why does it do it only for the "dynamically loaded" icons (from a stream) and what's the proper solution?

Also: what's the "proper way of storing icons"? My application will use some icons it loaded from the webserver. I most certainly don't want to load them every time. Of course I could keep the raw data around and re-create icons when I need them. That would be a REAL pain, though (creating and freeing all the time and spread across several units).
Which resources does a TIcon-object use? Isn't that supposed to create and release the handle as needed?? Does keeping a dozen TIcon's do any harm?

I still don't quite understand why someIcon.Handle := oldIcon.Handle releases the old handle??

I'm an "old Pascal programmer" but have mostly (>90%) worked in Java lately. And I must say: While I like Delphi for its performance and direct access to all kinds of stuff (*g*), I really think it's less clear than Java - not being able to tell the difference between a variable and a procedure/function/property is driving me crazy!! :-(( So much "unpredictable" stuff going on because of a "seemingly" simple assignment...
0
 
LVL 20

Expert Comment

by:Madshi
ID: 7021226
Well, if it would be a simple assignment, it would only change a variable, but not really change the application icon. That would not satisfy you, either, would it?   (-:

Maybe you can't clearly see when you do a simple assignment and when not. But generally, when setting a class/object property, it's almost never a simple assignment. Also Delphi is at least clearer than C++. Don't know about Java, though.

>> Why does the application do this? Why does it do it only for the "dynamically loaded" icons (from a
stream).

I'm not sure why your other icon didn't get freed, too. Theoretically it should have been freed, as well. Why does Delphi do that? Well, easy, to stop stupid programmers (no, I don't mean you) from accidently consuming all resources...   :-)

>> Which resources does a TIcon-object use?

Each icon consumes 2 GDI handles, one for the bitmap, one for the mask.

>> what's the "proper way of storing icons"?

A resource friendlier solution would be to store all the icons in one image list. An image list consumes also 2 GDI handles, like one icon does, but regardless of how many icons the image list contains.

Then you can use your "Application.Icon.Handle := " approach again and use ImageList_GetIcon to get an icon handle from the image list. Please check your system resources. You should do that in win9x. If you don't know how, download my free little utility, it shows you the current state of the resources and refreshes automatically:

http://www.madshi.net/showRes.zip

This utility doesn't work in NT/2k/XP, only in 9x/ME. It might crash when you close it, but don't care, that doesn't mean anything.

Regards, Madshi.
0
 
LVL 2

Author Comment

by:Ronald112197
ID: 7022459
> Well, if it would be a simple assignment, it would only change a variable, but not really
> change the application icon. That would not satisfy you, either, would it?   (-:

Well... that depends on who uses the variable :-)

> Maybe you can't clearly see when you do a simple assignment and when not.
> But generally, when setting a class/object property, it's almost never a simple assignment.

I don't have a "conceptual problem" with that. I just don't like the fact that you can't even tell a variable from a procedure call! That's (occasionally) HORRIBLE if you're trying to write efficient code. You think you're simply copying a variable and in reality, you're calling an expensive function. On the flip side, copying stuff (values) to local variables may not be necessary at all.
You just can't tell without some "investigation"
Also, (automatically) freeing variables is kind of a drastic measure!!
To the "innocent view", A.Handle := B.Handle looks like copying 32 bit.
In fact, "Handle" is a procedure-call (for both read AND write) and somehow, A afterwards invalidates B *shudder*

> Also Delphi is at least clearer than C++. Don't know about Java, though.

Well... I'm just not sure if C++ should be our benchmark for "readable code" *ggg*


>> Why does the application do this?
>Why does Delphi do that? Well, easy, to stop stupid programmers (no, I don't mean you)

Lucky you!! :-))

> from accidently consuming all resources...   :-)

I think my questions clearly show that I'd never do such a hideous thing *bg*

>> Which resources does a TIcon-object use?
>Each icon consumes 2 GDI handles, one for the bitmap, one for the mask.

They both count as bitmaps, right?

>> what's the "proper way of storing icons"?
> A resource friendlier solution would be to store all the icons in one image list.
> An image list consumes also 2 GDI handles, like one icon does,
> but regardless of how many icons the image list contains.

Does a TIcon ever free its handle automatically? Something in the code looks like it does. Maybe it just "lazily allocates" the handle... I don't *really* feel like digging through all these sources without a problem at hand...
=> It's funny though. It looks like there are not as many resources in use as the number of icons would suggest...

> Please check your system resources. You should do that in win9x.
I don't have Win9x any more. Yes, there's a CD somewhere, but no running system. Never needed it...

> This utility doesn't work in NT/2k/XP, only in 9x/ME.

Tough luck. But it's not so bad: I do have (and use!) MemorySleuth that came with Delphi 4. I think I'm doing quite OK - I sometimes have a timer or an icon that's not freed. Really weird! I mean: Sometimes nothing at all is left over, sometimes it's a timer (which I don't create myself at all - just have two of them on my form!!!) and sometimes it's an icon...
When it's an icon, MemorySleuth points me to the line where I do Application.Icon := myIcon;
I don't know why that happens! Does the application not free its own icon? Could that happen because I change it at runtime?

Anyway: It lists "GDI resources used by application" and has a top values of 9 bitmaps. How does that correspond to at least eight icons? (4 are embedded resources, 2 loaded from a stream and two Windows-standard icons)

I'll keep an eye on that.
Maybe I'll try an image list just to see how resource usage changes...

Thanks for your help so far. My "followup" questions are purely optional and partly "conversation" :-)) I don't think you're here for the points either, are you? Some people are so uptight and fight for the points --- like you could use them to buy a pizza :-))) I also have an "expert profile" here (I sometimes keep asking and answering questions separate...) and I never cared SO much about points - if I help somebody a lot I want to get the points as a bonus, but it's not what I'm here for :-)

Btw: I looked at your website and we seem to have some things in common :-)) - our age, our country, our bank, just to name a few *g*. Also our Atari ST and a way of porting stuff from Atari to DOS to Windows... However, I got my PC later than you: I started with a 386 and 50 MB hdd :-)))
I downloaded "fruits" - I'll have to try if that works in NT :-) (which version of DirectX does it need?)

Sincerely,
             Ronald
0
 
LVL 20

Expert Comment

by:Madshi
ID: 7023331
>> To the "innocent view", A.Handle := B.Handle looks like copying 32 bit.
>> In fact, "Handle" is a procedure-call (for both read AND write) and somehow, A afterwards invalidates
B *shudder*

No!! Either you have a typo or you understood it wrong. This assignment does NOT invalidate B!!

Look if you're doing this:

var str : string;
begin
  str := IntToStr(1);
  str := IntToStr(2);

First of all "str" is filled with "1". Afterwards it is filled with "2". Now what happens to the dynamically allocated "1" string? Delphi has to free it, because you don't, right? Does that make you *shudder*? It does not make *me* shudder. That's intelligent garbage collection, and I think Java does it, too.

Now the same happens to the icons. If you do:

Application.Icon.Handle := iconHandle1;
Application.Icon.Handle := iconHandle2;

Delphi now does the same thing as with the strings. Delphi destroys iconHandle1, as soon as you assign a new icon handle. That's again garbage collection. Well, in this case it is not that obvious, and that is the real problem. But nevertheless it does not make me shudder...

So to come back to your typo or misunderstanding: Delphi does NOT invalidate B, it invalidates only A.

>> >> Each icon consumes 2 GDI handles, one for the bitmap, one for the mask.
>> They both count as bitmaps, right?

Right.

>> Does a TIcon ever free its handle automatically?

Yes. If you free it.

>> Maybe it just "lazily allocates" the handle... I don't *really* feel like digging through all these sources without a problem at hand...

Well, in NT/2k/XP you have no big problem, there GDI resources are not that limited as in win9x.

>> It's funny though. It looks like there are not as many resources in use as the number of icons would
suggest...

I'm not too sure how TIcon works internally, because I use pure APIs most of the time instead of TIcon. Maybe it somehow saves handles, if possible.

>> When it's an icon, MemorySleuth points me to the line where I do Application.Icon := myIcon;
>> I don't know why that happens! Does the application not free its own icon? Could that happen because I change it at runtime?

No idea...

>> Anyway: It lists "GDI resources used by application" and has a top values of 9 bitmaps. How does that correspond to at least eight icons? (4 are embedded resources, 2 loaded from a stream and two Windows-standard icons)

I think Windows-standard icons are fix constansts, no real GDI handles. Then "stream-icons" are perhaps handled differently than others, no idea. Perhaps Delphi allocates a handle for stream-icons only in the moment where the handle property is being called? Just a guess...

>> I don't think you're here for the points either, are you?

Not mainly, it's just a kind of hiscore...

>> Btw: I looked at your website and we seem to have some things in common :-)) - our age

Ehmm... I should update my homepage...   :-)

>> our country, our bank, just to name a few *g*. Also our Atari ST and a way of porting stuff from Atari to DOS to Windows...

:-)

>> However, I got my PC later than you: I started with a 386 and 50 MB hdd :-)))

Wow, what luxury!

>> I downloaded "fruits" - I'll have to try if that works in NT :-) (which version of DirectX does it need?)

Phew, don't know. I think DX5 is enough, not sure, though...

Regards, Madshi.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
This video discusses moving either the default database or any database to a new volume.
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now