Delphi - A Windows Shell titkai

WinShell 3. rész

forráskód letöltése
Ön szerint egy adott állomány kiterjesztéshez - mint például a TXT - lehet-e több, különböző ikont rendelni egyidőben?

Ha nemre tippel, akkor bizony rosszul gondolja.

Legyen mondjuk három állományunk a.txt, b.txt, c.txt. Ekkor mind a három állományhoz teljesen különböző ikonokat rendelhetünk hozzá, ráadásul dinamikusan, bármilyen egyedi feltételrendszer alapján.

Hogy mire jó az, hogy egy állomány típusnak különböző ikonjai legyenek? Gondoljunk például egy olyan megoldásra, hogy az ikon képén keresztül jelölhetjük azokat az állományokat melyek még feldolgozásra várnak és egy másik képpel azokat, melyeket már feldolgozott a felhasználó. A mellékelt példa kipróbálásához az alábbi lépésekre van szükség:
1. Nyissa meg a ShellIconExt.dpr-t.
2. Javítsa a Unit1.pas 41. sorában lévő ICONPATH konstanst. Az itt megadott könyvtár mutasson arra, ahová a mellékelt példát helyezte.
3. A Project - Build menüponttal fordítsa le. Ekkor létrejön a ShellIconExt.dll.
4. Nyissa meg szerkesztésre a mellékelt ShellIconExt.reg állományt. Például egy Windows Intézővel keresse meg. Jobb gombbal kattintson rá, majd válassza az Edit menüpontot.
5. Ebben az állományban a @="D:\\Dso\\0247\\ShellSecret03\\ ShellIconExt.dll" sorban található elérési útvonalat javítsa ki arra, ahová a mellékelt példaprogramot helyezte. Lényeg az, hogy a ShellIconExt.dll-nek itt korrektül meg legyen adva a helye. Ügyeljen arra is, hogy az elérési útvonal megadásánál a \ jelet duplázva kell használnia!
6. Mentse és zárja a ShellIconExt.reg állományt.
7. Kattintson rá a ShellIconExt.reg állományra. Ekkor a Windows megkérdezi, hogy szeretné-e tartalmát a regisztrációs adatbázishoz hozzáfűzni. Erre igennel válaszoljon.
8. A következő rendszer indítása után a *.sec3 kiterjesztésű állományokhoz tartozó ikon más és más lesz attól függően, hogy az állomány nevüknek mi az utolsó betűje.

Mivel a mellékelt példához csupán három különböző ikont készítettünk (a.ico, b.ico, c.ico), így csak ezek használhatók fel a sec3 kiterjesztésű állományokhoz.

Ha ezek után létrehoz egy állományt bármelyik könyvtárba sec3 kiterjesztéssel, akkor a hozzárendelt ikon úgy változik, hogy mit adott az állomány név utolsó betűjének.

Példaképpen hozza létre a következő állományokat:
a.sec3
c.sec3
123a.sec3
4567b.sec3
Létrehozás legegyszerűbb módja, ha például egy intézővel valamely mappában jobb gombbal kattint, majd az Új - Szöveges állomány (New - Text document) menüpont választásával létrehoz egy állományt és ezt átnevezi a kiterjesztésével együtt.

További trükk, hogy elkerülhető a DLL installálása után a rendszerindítás abban az esetben, ha nem egy REG állományon keresztül végezzük el azt, hanem programból, például a TRegistry osztály felhasználásával. Miután a regisztráció megtörtént, akkor csupán az alábbi függvényt kell meghívni és a Windows Intézője frissíteni fogja az információt, így tudomást szerez a DLL-ünkről is.
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil);
A SHChangeNotify függvény használatához hivatkoznunk kell a ShlObj unit-ra.


A DLL megvalósítása hasonlóan történik, mint a cikksorozat előző részeiben. Most viszont új, eddig nem használt interfészek felhasználására lesz szükségünk. Ez az IPersistFile és az IExtractIcon lesz.
  TShellIcon = class(TComObject, IPersistFile, IExtractIcon)
  private
    FFileName, FIcon: string;

  protected
  {IPersistFile}
    function GetClassID(out classID: TCLSID): HResult; stdcall;
    function IsDirty: HResult; stdcall;
    function Load(pszFileName: POleStr; dwMode: Longint):
         HResult; stdcall;
    function Save(pszFileName: POleStr; fRemember: BOOL): 
         HResult; stdcall;
    function SaveCompleted(pszFileName: POleStr): HResult;
        stdcall;
    function GetCurFile(out pszFileName: POleStr): HResult; 
        stdcall;

  {IExtractIcon}
    function GetIconLocation(uFlags: UINT; szIconFile: PAnsiChar; 
       cchMax: UINT; out piIndex: Integer; out pwFlags: UINT): 
       HResult; stdcall;
    function Extract(pszFile: PAnsiChar; nIconIndex: UINT;
       out phiconLarge, phiconSmall: HICON; nIconSize: UINT):
       HResult; stdcall;

  public
    constructor Create;

  end;
A szokásos módon most is létrehozunk egy új osztályt, mely a két interfész mellett a TComObject-ből származik. Ezek után deklaráljuk az interfészek metódusait, melyek közül a jelen feladatunkhoz nem lesz szükség az összesre. Ezeknél csak egy E_FAIL visszatérési értéket adunk majd.

Amire igazán szükségünk lesz az a IPersistFile-ból a Load és az IExtractIcon-ból a GetIconLocation.

Nézzük először a Load függvényt. Amikor a Windows Intézője megjeleníti az állományokat, akkor kerül meghívásra a DLL-ünk Load függvénye. Itt a pszFileName paraméterben kapjuk meg az adott állomány nevét. Ezt el is tároljuk a globális FFileName változónkba. Ezek után meghatározzuk az állomány név utolsó karakterét és ezt eltároljuk az FIcon változóba, mivel ez határozza meg majd a későbbiek folyamán, hogy milyen ikont rendelünk az adott állományhoz.
function TShellIcon.Load(pszFileName: POleStr; 
       dwMode: Integer): HResult;
var
  a: array[0..MAX_PATH] of char;
begin
  WideCharToMultiByte(CP_ACP, 0, pszFileName,
        -1, a, MAX_PATH, nil, nil);
  FFileName:=a;
  FIcon:=ExtractFileName(FFileName);
  FIcon:=LowerCase(FIcon[Length(FIcon)-5]);
  result:=NOERROR;
end;
Természetesen más módszer is választható arra nézve, hogy miként kerüljön állományonként más és más ikon megjelenítésre. Az is megoldható, hogy megnyitjuk az állományt a Load-nál és az adott tartalma alapján döntjük el, hogy melyik ikont rendeljük hozzá.


A GetIconLocation kerül meghívásra mikor ténylegesen döntenünk kell arról, hogy melyik ikont rendeljük az adott állományhoz. Ekkor az szIconFile paraméterben adhatjuk meg a kérdéses ikon állományt (xy.ICO) teljes elérési útvonallal együtt. Itt például megadhatnánk egy DLL, vagy EXE nevét is, hiszen ezekben is tárolhatunk ikont, ráadásul nem is egyet. Több ikon esetén a piIndex paraméterben rendelkezhetünk arról, hogy a megadott állomány hányadik ikonját használja fel a Windows.
function TShellIcon.GetIconLocation(uFlags: UINT; szIconFile: 
       PAnsiChar; cchMax: UINT; out piIndex: Integer; 
       out pwFlags: UINT): HResult;
begin
  StrPCopy(szIconFile, ICONPATH+FIcon+'.ico');
  piIndex:=0;
  result:=S_OK;
end;

Hasonlóan az előzőekhez ezt a DLL-t is regisztrálnunk kell a Windows regisztrációs adatbázisába. Ezt szinte ugyanúgy tehetjük, mint ahogy azt a cikksorozat első részében leírtunk.

Ahogy az a ShellIconExt.reg-ban is láthatjuk, most az IconHandler kulcs alá kell a bejegyzéseinket elhelyezni és nem a ContextMenuHandlers, vagy a PropertySheetHandlers alá.




WinShell cikksorozat