Lars Bargmann
Lars Bargmann

Reputation: 142

Threaded loading of icons in Delphi

Using Delphi 2009, trying to make a launcher. In order to make it "snappy" I would really like to load icons in a background thread.

I have used the solution found here : Can 48x48 or 64x64 icons be obtained from the Vista Shell?

This works fine, if NOT run in a thread. As soon as I put it in a thread, some icons are not "fetched", or being some kind of generic icon. I even tried serializing the threads (making them obsolote, in effect) but it yields the same results.

So, the question is: How do I load icons (with the same available options as the linked example) in a thread?

/Lars

Edit: Added some very basic error-checking in GetIconFromFile

if SHGetFileInfo( PChar( aFile ),
                FILE_ATTRIBUTE_NORMAL,
                SFI,
                SizeOf( TSHFileInfo ),
                SHGFI_ICON
                  or SHGFI_LARGEICON
                  or SHGFI_SHELLICONSIZE
                  or SHGFI_SYSICONINDEX
                  or SHGFI_TYPENAME
                  or SHGFI_DISPLAYNAME ) <> 0 then
begin
  if not Assigned( aIcon ) then
    aIcon := TIcon.Create;
  aImgList := GetImageListSH( SHIL_FLAG );
  aIndex := SFI.iIcon;
  if aImgList <> 0 then
    aIcon.Handle := ImageList_GetIcon( aImgList, aIndex, ILD_NORMAL );
end;

This doesn't make any diffenrence. I am still getting some generic icons (only when this is called in a thread, though)

Edit2 : Threading-code (very simple) :

procedure TIconLoader.Execute;
var
  Item : TGridItem;
  I : TIcon;
begin
  inherited;

  while not terminated do
  begin
    Item := nil;
    if assigned(FOnNeedGridItem) then
    begin
      Synchronize(
          procedure
          begin
            FOnNeedGridItem(Self, Item);
          end
      );
    end;

    if assigned(Item) then
    begin
      GetIconFromFile(Item.FFilename, I, SHIL_EXTRALARGE);
      Synchronize(
          procedure
          begin
            Item.SetIcon(I);
          end
      );
//      I.Free;
    end
    else
      Terminate;
  end;
end;

Upvotes: 4

Views: 606

Answers (3)

David Heffernan
David Heffernan

Reputation: 612934

The documentation for SHGetFileInfo specifies that you must initialize COM before calling the function. Since COM initialization is per-thread, you need to initialise COM in the thread. That means calling CoInitialize or CoInitializeEx from the Execute method of the thread.

Upvotes: 2

Nahk Cogn
Nahk Cogn

Reputation: 31

Try moving this:

GetIconFromFile(Item.FFilename, I, SHIL_EXTRALARGE);

into synchronize block. And you should call DestroyIcon() after using SHGetFileInfo().

Upvotes: -1

mjn
mjn

Reputation: 36644

Maybe you can simplify your threading code by using the TThread.Queue methods introduced in Delphi 2009.

An article which gives some examples and background information is here:

Synchronize and Queue with Parameters

TThread.Queue is non-blocking. This allows to sequentially create background worker threads for all your icons and then let them load asynchronously.

Upvotes: 0

Related Questions