SysImageList

Please post bug reports, feature requests, or any question regarding the DELPHI AREA products here.

SysImageList

Postby w2m » November 15th, 2008, 3:25 pm

I am running SysImageList in Delphi 2009 on Windows Vista.
I am developing a zip file application using the SourceForge zip component.
I need add the icon associated with the file stored in the zip file and get the index of the associated icon to add the icon to a TListview that contains the zip file information.
The filename in the zip file does not exist on the disk only in the zip file.

When I run the SysImageList demo that gets the system image index and displays the associated icon the index (6) and the icon is the same for these for 'bogusfile.pas", 'bogusfile.dpr"and 'bogusfile.dfm".

I do not understand system image lists very well; however, when I load the SysImageList icons into a list box it only contains 6 icons.

if I build my own system image list I get 620 icons but I have to call FileIconInit to get all the system icons.

Is the SysImageList component supposed to hold all the system's icons or only a few of the icons?


Code: Select all
// Reinitializes the system image list.
// Set FullInit to TRUE to restore the system image cache from disk; FALSE otherwise.
function FileIconInit( FullInit: BOOL ): BOOL; stdcall;
type
  TFileIconInit = function( FullInit: BOOL ): BOOL; stdcall;
var
  ShellDLL: HMODULE;
  PFileIconInit: TFileIconInit;
begin
  Result := False;
  if ( Win32Platform = VER_PLATFORM_WIN32_NT ) then
  begin
    ShellDLL := GetModuleHandle( PChar( Shell32 ) );
    PFileIconInit := GetProcAddress( ShellDLL, PChar( 660 ) );
    if ( Assigned( PFileIconInit ) ) then
      Result := PFileIconInit( FullInit );
  end;
end;

function GetSystemSmallIconsList: TCustomImageList;
// This gets the system image list.
var
  SysIL: HImageList;
  SFI: TSHFileInfo;
  MyImages: TCustomImageList;
begin
  // This gets the small image list
  SysIL := SHGetFileInfo( '', 0, SFI, SizeOf( SFI ), SHGFI_SYSICONINDEX or SHGFI_SMALLICON );
  if SysIL <> 0 then
  begin
    MyImages := TCustomImageList.Create( nil );
    // Assign the system list to the component
    MyImages.Handle := SysIL;
    // The following prevents the image list handle from being
    // destroyed when the component is.
    MyImages.ShareImages := TRUE;
    Result := MyImages;
  end;
end;

function GetSystemLargeIconsList: TCustomImageList;
// This gets the system image list.
var
  SysIL: HImageList;
  SFI: TSHFileInfo;
  MyImages: TCustomImageList;
begin
  // This gets the large image list.
  SysIL := SHGetFileInfo( '', 0, SFI, SizeOf( SFI ), SHGFI_SYSICONINDEX or SHGFI_LARGEICON );
  if SysIL <> 0 then
  begin
    MyImages := TCustomImageList.Create( nil );
    // Assign the system list to the component
    MyImages.Handle := SysIL;
    // The following prevents the image list handle from being
    // destroyed when the component is.
    MyImages.ShareImages := TRUE;
    Result := MyImages;
  end;
end;

// Return the SystemIconIndex baised on the FileExtension
// Return the SystemIconIndex baised on the FileExtension
// If the uFlags parameter includes the SHGFI_USEFILEATTRIBUTES flag,
// this parameter does not have to be a valid file name.
// The function will proceed as if the file exists with the specified name and with
// the file attributes passed in the dwFileAttributes parameter.
// This allows you to obtain information about a file type by passing just the extension
// for pszPath and passing FILE_ATTRIBUTE_NORMAL in dwFileAttributes.
function GetIconIndex( FileExtension: string ): integer;
var
  SFI: TSHFileInfo;
begin
  Result := -1;
  ZeroMemory( @SFI, SizeOf( SFI ) );
[b]  SHGetFileInfo( PChar( 'bogusfile' + FileExtension ), FILE_ATTRIBUTE_NORMAL or SHGFI_SYSICONINDEX, SFI, SizeOf( SFI ), [/b]SHGFI_USEFILEATTRIBUTES );
  Result := SFI.iIcon;
end;

procedure TForm1.FormCreate( Sender: TObject );
begin
[b]  FileIconInit( true );[/b]
  SystemImageList := GetSystemSmallIconsList;
  ListView1.SmallImages := SystemImageList;
end;

procedure TForm1.FormDestroy( Sender: TObject );
begin
  SystemImageList.Free;
end;

procedure TForm1.FormShow( Sender: TObject );
var
  i: integer;
  ListItem: TListItem;
begin
  dxImageListBox1.ImageList := SystemImageList;
  for i := 0 to SystemImageList.Count - 1 do
  begin
    dxImageListBox1.Items.Add( IntToStr( i ) );
    dxImageListBox1.ImageIndexes[ i ] := i;
  end;
end;


If I set the IconSet property to isSystem then double click the SysImageList component in the Delphi 2009 ide I get an exception - "Failed to write ImageList data to stream".
If I change the IconSize property to isSmallIcons from isLargeIcons the height and width property does not change from 32x32 to 16x16.

I can not get the associated icon for some file types with the SysImageList component.
I can not get the associated icon index with my own function GetIconIndex( FileExtension: string ): integer; with my own systemimagelist. Get GetIconIndex always returns 0 for SFI.iIcon.

The approach you used to get the iconindex is to create a temporary file when the filename does not exist. The documentation for SHGetFileInfo says that If the uFlags parameter includes the SHGFI_USEFILEATTRIBUTES flag, this parameter does not have to be a valid file name. You do not use this parameter when calling SHGetFileInfo.

I am stuck. Neither the SysImageList component or my own code is working.
Any assistance to fix SysImageList component or my own code would be apprecated.

Regards,
Bill
w2m
w2m
Senior Member
Senior Member
 
Posts: 76
Joined: March 8th, 2003, 7:11 pm
Location: New York, USA

Re: SysImageList

Postby Kambiz » November 15th, 2008, 4:22 pm

SysImageList does not have any stored image in itself. It is only a wrapper to have access to Windows system image list in Delphi way. Because of that you got an error message when you double clicked on it.

Before Windows XP, the Windows was adding all the icons to the image list on the system startup, and the image list was shared between processes. On Windows XP (and Vista) the icons added to the list when they are needed, and each process has its own copy of the system image list. This is why you get only a few icons.

Regarding files' accosiated icons, according to my tests, using a temporary file is the only solution that works. Actually I still get the correct icon on Vista even for non existing files. I guess file associations of Delphi on your machine is corrupted and because of that you get the same icon for .dpr, .pas, and .dfm extensions.

SysImageList has one problem: it is not thread safe. Can this be your case?
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2408
Joined: March 7th, 2003, 7:10 pm

Re: SysImageList

Postby w2m » November 15th, 2008, 5:13 pm

No. I am not using any threads that I am aware of.

Is there a problem with GetUniqueFileName in sysimage.pas with Delphi 2009? I think there maybe a problem?

Code: Select all
 function GetUniqueFileName: string;
  var
    TempPath: array[ 0..255 ] of Char;
    TempFile: array[ 0..255 ] of Char;
  begin
    GetTempPath( SizeOf( TempPath ), TempPath );
    GetTempFileName( TempPath, 'SIL', 0, TempFile );
    Result := TempFile;
  end;


a potential replacement is:
Code: Select all
function GetTempFileName( const Stub: string; const Create: Boolean ): string;
  begin
    // Get temporary folder
    SetLength( Result, Windows.MAX_PATH );
    Windows.GetTempPath( Windows.MAX_PATH, PChar( Result ) );
    // Get unique temporary file name (it is created as side effect of this call)
    if Windows.GetTempFileName(
      PChar( Result ), PChar( Stub ), 0, PChar( Result )
      ) <> 0 then
    begin
      // Succeeded
      Result := PChar( Result );
      if not Create then
        // user doesn't want file creating: so we delete the file
        SysUtils.DeleteFile( Result );
    end
    else
      // Failed
      Result := '';
  end;


Does your demos work ok with Delphi 2009 on Vista?
I am having all sorts of problems with SysImageList?

Bill
w2m
w2m
Senior Member
Senior Member
 
Posts: 76
Joined: March 8th, 2003, 7:11 pm
Location: New York, USA

Re: SysImageList

Postby w2m » November 15th, 2008, 5:24 pm

I have attached a zip file containing the project. It is vcl baised for Delphi 2009 only. The only thing you will need to compile it is the ZipForge Component. You can download the component here:

http://www.componentace.com/

On that page click on the ZipForge Download link.

Do you think SysImageList is working ok on your system with Delphi 2009 and Vista?

Thanks,

Bill
Attachments
EditArchive.zip
(180.13 KiB) Downloaded 118 times
w2m
w2m
Senior Member
Senior Member
 
Posts: 76
Joined: March 8th, 2003, 7:11 pm
Location: New York, USA

Re: SysImageList

Postby Kambiz » November 15th, 2008, 6:05 pm

:D Hopefully was quite easy to resolve the issue.

  1. Assign SysImageList1 only to SmallImages property of the ListView control. << This one makes your application works.
  2. If you need also images for LargeImages property of the ListView, use another instance of SysImageList component and optionally set its IconSize property to isLargeIcons.
You do not need to check whether the index is less than the images count or not, it is always less than that. Safely assign the returned value to listitem's imageindex property.

Code: Select all
ListItem.ImageIndex := SysImageList1.ImageIndexOf( ArchiveItem.FileName, False );

Regarding the temporary file, yes, that part is not thread safe and I will fix it soon. However, the component rarely needs to create a temporary file.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2408
Joined: March 7th, 2003, 7:10 pm

Re: SysImageList

Postby w2m » November 15th, 2008, 6:18 pm

Wow... that fixed it!

Do you think that GetTempFileName is any better than GetUniqueFileName or does it not make any difference for Delphi 2009?

Thanks for your help

Bill

Code: Select all
function TSysImageList.ImageIndexOf( const Path: string; OpenIcon: Boolean ): Integer;

 // w2m
 function GetTempFileName( const Stub: string; const Create: Boolean ): string;
  begin
    // Get temporary folder
    SetLength( Result, Windows.MAX_PATH );
    Windows.GetTempPath( Windows.MAX_PATH, PChar( Result ) );
    // Get unique temporary file name (it is created as side effect of this call)
    if Windows.GetTempFileName(
      PChar( Result ), PChar( Stub ), 0, PChar( Result )
      ) <> 0 then
    begin
      // Succeeded
      Result := PChar( Result );
      if not Create then
        // user doesn't want file creating: so we delete the file
        SysUtils.DeleteFile( Result );
    end
    else
      // Failed
      Result := '';
  end;
// w2m
end;


Code: Select all
 function GetUniqueFileName: string;
  var
    TempPath: array[ 0..255 ] of Char;
    TempFile: array[ 0..255 ] of Char;
  begin
    GetTempPath( SizeOf( TempPath ), TempPath );
    GetTempFileName( TempPath, 'SIL', 0, TempFile );
    Result := TempFile;
  end;
w2m
Senior Member
Senior Member
 
Posts: 76
Joined: March 8th, 2003, 7:11 pm
Location: New York, USA

Re: SysImageList

Postby Kambiz » November 15th, 2008, 6:38 pm

You're welcome!

Both codes suffering from a similar problem: it is possible that another thread or process creates a temporary file with the same name.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2408
Joined: March 7th, 2003, 7:10 pm

Re: SysImageList

Postby w2m » November 15th, 2008, 7:29 pm

Would using the shell solve the thread problem?

How you you change imagelists if you click on a column to sort? I tried changing the image list to one that has arrows in it for just sorting but that also sets the items images as well.

Bill
w2m
w2m
Senior Member
Senior Member
 
Posts: 76
Joined: March 8th, 2003, 7:11 pm
Location: New York, USA

Re: SysImageList

Postby w2m » November 15th, 2008, 9:34 pm

I figured out drawing the column icons while heeping SysImageListSmall icons for items:

Code: Select all
procedure TFormMain.ListViewColumnClick( Sender: TObject; Column: TListColumn );
var
  i: integer;
begin
  Screen.Cursor := crHourglass;
  try
    if ListView.ViewStyle = vsReport then
    begin
     // use triangles imagelist for sorting columns
      ListView.SmallImages := ImageList2;
   end;
  finally; Screen.Cursor := crDefault; end;
end;


Code: Select all
// Use SysImageList to draw items
procedure TFormMain.ListViewAdvancedCustomDrawItem( Sender: TCustomListView; Item: TListItem; State: TCustomDrawState;
  Stage: TCustomDrawStage; var DefaultDraw: Boolean );
begin
  if Stage = cdPrePaint then
    ListView.SmallImages := SysImageListSmall;
end;


I tried to show large icons but this fails... large icons are always shown:
Code: Select all
if ListView.ViewStyle = vsIcon then begin
  ListView.LargeImages := SysImageListLarge;
  ListView.SmallImages := nil
end
else
begin
  ListView.SmallImages := SysImageListSmall;
  ListView.LargeImages :=nil;
end;


Bill
w2m
w2m
Senior Member
Senior Member
 
Posts: 76
Joined: March 8th, 2003, 7:11 pm
Location: New York, USA

Re: SysImageList

Postby Kambiz » November 16th, 2008, 9:39 am

I released the update of SysImageList. It's thread safe now.

I checked it with your program and it works, even with large icons.

I suggest to assign appropriate SysImageList instances to SmallImages and LargeImages properties at design time.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2408
Joined: March 7th, 2003, 7:10 pm

Re: SysImageList

Postby w2m » November 16th, 2008, 4:08 pm

The update is working much better than the previous version.
Thank you.

Bill
w2m
w2m
Senior Member
Senior Member
 
Posts: 76
Joined: March 8th, 2003, 7:11 pm
Location: New York, USA


Return to DELPHI AREA Products

Who is online

Users browsing this forum: No registered users and 1 guest