![]() |
Icons aus Shell32.dll
Wie kann ich die Laufwerksymbole aus der Shell32.dll auslesen?
|
Moin Tpercon,
ich hab' hier mal eine etwas umfangreichere Lösung:
Code:
Welche Kompos Du brauchst ergibt sich wohl aus der Deklaration von TForm1.
unit MAIN;
interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Menus, ImgList, ComCtrls, ExtCtrls,filectrl; type TForm1 = class(TForm) OpenDialog1: TOpenDialog; MainMenu1: TMainMenu; ffnen1: TMenuItem; ImageList1: TImageList; PageControl1: TPageControl; TabSheet1: TTabSheet; ListView1: TListView; TabSheet2: TTabSheet; ImageList2: TImageList; ListView2: TListView; procedure ffnen1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private-Deklarationen } FSaveAsIcon : Boolean; public { Public-Deklarationen } property SaveAsIcon : Boolean read FSaveAsIcon write FSaveAsIcon; end; var Form1: TForm1; type PHICON = ^HICON; TcsExtractIconEx = function( const lpszFile : PChar; const nIconIndex : integer; const phiconLarge : PHICON; const phiconSmall : PHICON; const nIcons : DWORD ) : DWORD; stdcall; var ExtractIconEx : TcsExtractIconEx; implementation {$R *.DFM} var hDLL : DWORD; procedure TForm1.ffnen1Click(Sender: TObject); var pLarge : PHICON; pSmall : PHICON; pWork : PHICON; icoWork : TIcon; dwCount : DWord; i : integer; begin if OpenDialog1.Execute then begin Caption := '['+OpenDialog1.FileName+']'; ImageList1.Clear; ImageList2.Clear; ImageList1.Width := GetSystemMetrics(SM_CXSMICON); ImageList1.Height := GetSystemMetrics(SM_CYSMICON); ImageList2.Width := GetSystemMetrics(SM_CXICON); ImageList2.Height := GetSystemMetrics(SM_CYICON); ListView1.Items.Clear; ListView2.Items.Clear; ListView1.Items.BeginUpdate; ListView2.Items.BeginUpdate; try dwCount := ExtractIconEx(PChar(OpenDialog1.FileName),-1,nil,nil,0); pLarge := AllocMem(dwCount*SizeOf(HICON)); pSmall := AllocMem(dwCount*SizeOf(HICON)); icoWork := TIcon.Create; try ExtractIconEx(PChar(OpenDialog1.FileName),0,pLarge,pSmall,dwCount); pWork := pSmall; for i := 0 to dwCount-1 do begin icoWork.Handle := pWork^; ImageList1.AddIcon(icoWork); ListView1.Items.Add; ListView1.Items[ListView1.Items.Count-1].ImageIndex := ImageList1.Count-1; ListView1.Items[ListView1.Items.Count-1].Caption := IntToStr(ImageList1.Count-1); inc(pWork); end; pWork := pLarge; for i := 0 to dwCount-1 do begin icoWork.Handle := pWork^; ImageList2.AddIcon(icoWork); ListView2.Items.Add; ListView2.Items[ListView2.Items.Count-1].ImageIndex := ImageList2.Count-1; ListView2.Items[ListView2.Items.Count-1].Caption := IntToStr(ImageList2.Count-1+ImageList1.Count); inc(pWork); end; finally FreeAndNil(icoWork); FreeMem(pLarge,dwCount*SizeOf(HICON)); FreeMem(pSmall,dwCount*SizeOf(HICON)); end; finally ListView1.Items.EndUpdate; ListView2.Items.EndUpdate; end; end; end; procedure TForm1.FormCreate(Sender: TObject); begin PageControl1.ActivePage := TabSheet1; end; initialization begin hDLL := LoadLibrary('shell32.dll'); if hDLL = 0 then begin ShowMessage('Laden von SHELL32.DLL fehlgeschlagen'); exit; end; @ExtractIconEx := GetProcAddress(hDLL,'ExtractIconEx'); if @ExtractIconEx = nil then begin ShowMessage('ExtractIconEx'); exit; end; end; finalization begin if hDLL <> 0 then begin FreeLibrary(hDLL); end; end; end. Delphi hat zwar eine Implementation von ExtractIconEx, die ist aber leider Fehlerhaft, so dass ein eigener Import notwendig wird. Mit diesem Code bekommst Du alle Icon's im OpenDialog (Filter auf EXE und DLL) angegebenen Datei angezeigt. Die ListViews müssen auf ViewStyle vsICON eingestellt sein. Das ist nur ein Fragment ;-) |
Auf Delphi 3000 habe ich einen Artikel geschrieben, der einen Weg zeigt das Icon jeder Datei/jedes Ordners zu ermitteln. Für die Laufwerke musst Du einfach den Laufwerkspfad C:\ übergeben.
![]() :cat: |
@ Christian:
Leider muß ich gezielt bestimmte Icons haben und jetzt weiß ich ja nicht, ob die Icons in jeder Shell32.dll genau an der gleichen Stelle sind?! @ sakura: Willst du nicht mal ein paar deiner Einträge auf Delphi 3000 nach hier übertrgen? Gruß |
Zitat:
bei 95, 98 und ME sind sie an der gleichen Stelle. Ich denke das es bei NT, 2k auch noch so sein wird, aber bei XP? Ich weiss nicht, dem trau ich alles zu. Man sollte aber auch leute mit XP nicht fördern. :twisted: :twisted: :twisted: |
Moin Tpercon,
also ich würde mal mit hoher Wahrscheinlichkeit davon ausgehen, dass die Icons, unabhängig vom OS, immer an der gleichen Stelle liegen. Bei XP könntest Du, da es hier ja auch noch die 48x48 und 64x64 gibt, wohl noch mehr finden. Wie das genau aussieht kann ich allerdings, mangels XP, nicht ausprobieren. Würde sich an den Positionen etwas ändern, hätte, unter Umständen wohl auch so manches Microsoft Produkt ein Problem. |
Meinst du denn z.B. das Laufwerksymbol ist immer an der 8ten Stelle und so?
|
Moin Tpercon,
würde ich mal von ausgehen. Was eventuell möglich wäre (ich kenne jetzt die Einstellmöglichkeiten von XP noch nicht): Da es unter XP ja auch noch weitere Grössen für die Icons gibt, könnte es sein, dass man sich jeweils die Anzahl der Icons der verschiedenen Grössen "merken" muss, und das gewünschte Icon hat dann den entprechenen Offset. Soll heissen: Es gibt vor den 16x16 Icons keine weiteren: Hier sollte die Nummer sich nicht ändern. Bei den 32x32 Icons wäre es dann: Anzahl 16x16 Icons plus der gewünschten Nummer. Beispiel: Unter W2K SP 1 gibt es 108 Small Icons (16x16, Nummer 0 - 107). Das Laufwerksicon ist das 9. Icon und hat somit die Nummer 8. Soll das entsprechende Large Icon angesprochen werden, muss die Anzahl der Small Icons dazugezählt werden, also 108 + 8 = 116. Mit dieser Methode spielt es dann wohl auch keine Rolle, ob sich die Anzahl der Icons vergrössert, denn dass es ein bestimmtes Icon nur in einer Grösse gibt erscheint mir für die Shell32 äusserst unwahrscheinlich. Wie's jetzt unter XP aussieht kann ich leider nicht genau sagen, aber da die Funktion ExtractIconEx nur zwei Tabellen kennt, vermute ich mal, dass hier der Unterschied nur in der Grösse von Small und Large besteht, dass also, nur als Beispiel, 32x32 als Small und 64x64 als Large angesehen werden. Das wäre dann durch GetSystemMetrics zu ermitteln, dürfte sich aber auf die Nummerierung nicht auswirken. Da es auch in den Headern keine Konstanten zu geben scheint, die weitere Icongrössen zulassen (so nach dem Motto Extra Large oder ähnlich) reichen die zwei Tabellen die ExtractIconEx zur Verfügung stellt dann auch. Nur wie gross nun Small bzw. Large ist hängt von den Systemeinstellungen ab. Du wirst Dich also wohl darauf verlassen können, dass Icon #8 immer das kleine Laufwerksicon ist, aber dieses muss nicht zwingend 16x16 gross sein. |
Zitat:
Es gab aber Unterschiede in den Positionen. Mal schau´n. Vielleicht habe ich die Liste noch ... Ansonsten muss ich noch mal die "shell32.dll" von XP auf die FAT32-Partition von 98 rüberziehen und den Inhalt mit MicroAngelo vergleichen. Also, in den meisten Fällen stimmen die Positionen überein ... aber davon ausgehen würde ich nicht. |
Wäre nett wenn du mal nach schauen würdest, denn wenn es größere Unterschiede gibt, dann brauch ich garnicht damit anzufangen sondern laß die Icons schon in der Anwendung.
Gruß |
Moin Mathias,
meinst Du jetzt die Smallicons? |
@Chris: Nö.
@Tpercon: Wenn du unter dem Imageindex 48 bleibst, dann kannst du die Icons getrost verwenden. Soweit ich das gesehen habe, sind diese identisch. Insofern dürfte es bei dir (Laufwerks-Icons) keine Probleme geben. Generell wäre aber sicher sakuras Beitrag interessant, weil diese Variante unabhängig von eigenen Zugriffen auf die "shell32.dll" ist. Ich habe mir mal die Freiheit genommen, den für dich wahrscheinlich wichtigen Teil zu extrahieren. sakura wird hoffentlich nicht schimpfen. Folgendes (gebasteltes :wink:) Beispiel liest die vorhandenen Laufwerke mit den korrekten Icons in eine ListView ein. Die Routinen "DriveExists" und "DriveType" stehen in der ![]()
Code:
Codebasis, Copyright by Sakura.
procedure TForm1.Button1Click(Sender: TObject);
var i : integer; FileInfo: TSHFileInfo; ImageListHandle: THandle; aIcon: TIcon; begin for i := 0 to 25 do if(DriveExists(i)) then begin fillchar(FileInfo,sizeof(FileInfo),#0); ImageListHandle := SHGetFileInfo(pchar(CHR(i+65) + ':\'), 0, FileInfo, sizeof(FileInfo), SHGFI_ICON or SHGFI_LARGEICON); try aIcon := TIcon.Create; try aIcon.Handle := FileInfo.hIcon; ListView1.Items.Add; ListView1.Items[ListView1.Items.Count-1].Caption := CHR(i+65) + ':\ (' + DriveType(i) + ')'; ListView1.Items[ListView1.Items.Count-1].ImageIndex := ImageList1.AddIcon(aIcon); finally FreeAndNil(aIcon); end; finally DestroyIcon(FileInfo.hIcon); ImageList_Destroy(ImageListHandle); end; end; end; (Das nur, damit er mich nicht auf 10 Mio US-Dollar verklagt. :idea: :twisted:) <Edit> "ShellAPI" und "CommCtrl" (für ImageList_Destroy) nicht vergessen! Außerdem sollte die Imageliste natürlich die Maße 32x32 besitzen. Andernfalls kann man aber auch das Flag SHGFI_SMALLICON benutzen. Hatte ich doch glatt vergessen zu erwähnen. :oops: </Edit> Gruß, Mathias. |
OK, funktionieren tut es wunderbar, nur es ist zu langsam.
Ob es was bringt wenn man überprüft ob ein Icon schon in der ImageList ist oder nicht und dann entsprechend das Icon nicht noch einmal bzw. doch einfügt. Oder woher kann dieser Geschwindigkeitsverlust sonst kommen? |
Zitat:
Ich gehe mal davon aus, dass du die Icons nicht ständig neu einliest. Wenn du das nur einmal beim Start machst, dann bietet sich doch ein Splashscreen mit einem Progressbar an. Dann weiß der User, dass er einen Moment Geduld haben muss. |
Die Laufwerksymbole lade ich natürlich nur beim Start.
Aber die Dateisymbole lade ich ständig neu. Wie denn sonst? Oder sollte ich sämtliche Dateisymbole auf dem System beim Start laden, die Position und die Extension mir in einem Array speichern um sie später zuordnen zu können? Gruß |
Was programmierst du eigentlich?
Oder anders: Warum musst du die Dateisymbole ständig neu laden? Ich glaube, bei Delphi6 gibt´s eine spezielle Listview, die dir die Ordner inkl. Symbolen usw. anzeigt. Damit würdest du sicher besser wegkommen, wenn du eine Datei- und Ordnerstruktur in deinen Programmen brauchst. |
Moin Tpercon,
ich hab' jetzt nich ausprobiert, wie sich das auf die Geschwindigkeit auswirkt, aber Du könntest bei den Dateiicons ja mal versuchen, Dir eine TStringsList mit den schon eingelesenen Extensions aufzubauen, (Sorted=true, Duplicates=dupIgnore), und die Extensions dann mittels AddObject hinzufügen, wobei Du als Objekt jeweils das Icon(Handle) hinzufügst, dass über ExtractAssociatedIcon ausgelesen wird. Dann liessen sich bereits bekannte Icons aus der Liste wohl relativ schnell auslesen lassen. Oder Du benutzt eine ImageList, der die jeweils neuen Icons hinzugefügt werden, und fügst als Objekt einfach nur die Nummer des Icons in der ImageList hinzu. (auslesen wie vorher). Damit könntest Du Dir die Verwaltung (Create/Free) der angehängten Iconobjekte sparen. |
Zitat:
das macht doch die TreeView!? :roll: Aber bei der ListView kann man ja auch eigene Symbole benutzen. Er hat D5Ent. Ich weiss nicht ob TreeView da schon dabei ist. |
Moin Daniel B,
der TreeView als solcher ist schon enthalten, nur nicht der ShellTreeView, den Mathias wohl meinte. |
Die ShellTreeView Komponente zeigt wohl automatisch schon die Icons der Dateien/ Ordner und Laufwerke an?
Aber ich möchte trotzdem die Dateien lieber in der ListView lassen und nur die Ordner/ Laufwerkauswahl über die TreeView machen. Wie kann man denn herausfinden, ob ein Icon schon in der ImageList ist oder nicht, ohne ne zusätzliche Liste zu benutzen?! Geht das? Momentan durchsuche ich nämlich die ListView nach der Extension und entscheide so, ob ich das Icon neu hinzufügen muß oder den Index benutzen kann und diese Methode ist schon etwas schneller als zuvor! |
Genau, Christian, mir fiel das Wort nicht ein. ShellTreeView. Aber gibt es dazu nicht auch das Gegenstück ShellListView? Das erscheint mir logisch. Das müsste einer klären, der Delphi6 hat.
|
Zitat:
also bei mir, D6PE, ist es da. ShellListView, ShellTreeView, ShellComboBox und ShellChangeNotifier. |
20 MIO...
Ein Nachteil hat das "Cachen" von Icons. Es gibt Dateitypen, welche für jede Datei ein anderes Icon nutzen (z.B. ICO). Wenn die gecacht werden dann ist es doof. (Kann man auch für BMP einstellen). |
Re: Icons aus Shell32.dll
Im Lizenzvertrag von WIndows steht (seit 2000 / ME sogar expliziet) das es verboten ist Icons von Windows in eigenen Anwendung zu integrieren!
Das nur anbei gesagt, hab ich mal in ner größeren Zeitschrift (glaub c´t) gelesen! Gruß martin W. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:32 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz