|
|
A Windows Shell titkai
WinShell 1. rész
|
|
Példaprogram letöltése
8712 bájt
|
Most induló cikksorozatunkban a Windows Shell titkait, nem vagy csak kevésbé dokumentált lehetőségeit kutatjuk.
A sorozatból így például megtudhatjuk majd, hogy miként rendelhetünk különböző ikonokat ugyanolyan kiterjesztéssel rendelkező állományokhoz. Fény derül arra is, hogy miként jeleníthetünk meg egyedi információkat egy-egy állományról, ha arra rámutat a felhasználó egy Windows Intézőben. Az első részben arra keresünk megoldást, hogy miként egészíthetjük ki azt a menürendszert, mely akkor jelenik meg, ha a felhasználó a Windows Intézőjében egy állományon jobb gombbal kattint. A tetszőlegesen megjelenő menüpontokhoz persze egyedi funkciókat is rendelhetünk. Gondoljunk csak például a WinZip hasonló módszerű szolgáltatásaira.
A mellékelt példa kipróbálásához az alábbi lépésekre van szükség:
1. Nyissa meg a ShellMenuExt.dpr és a Project - Build menüponttal fordítsa le. Ekkor létrejön a ShellMenuExt.dll.
2. Nyissa meg szerkesztésre a mellékelt ShellMenuExt.reg állományt. Például egy Windows Intézővel keresse meg. Jobb gombbal kattintson rá, majd válassza az Edit menüpontot.
3. Ebben az állományban a @="D:\\Dso\\0245\\ShellSecret01\\ShellMenuExt.dll" sorban található elérési útvonalat javítsa ki arra, ahová mellékelt példaprogramot helyezte. Lényeg az, hogy a ShellMenuExt.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!
4. Mentse és zárja a ShellMenuExt.reg állományt
5. Kattintson rá a ShellMenuExt.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.
6. Ezek után kattintson jobb gombbal a mellékelt a.sec állományon. A megjelenő menü első menüpontját már a saját ShellMenuExt.dll programunk szolgáltatja.
Fontos tudnivaló, hogy ha ezek után szeretnénk a DLL-ünket újra fordítani, akkor előtte be kell zárnunk az összes olyan alkalmazást, mely használja a ShellMenuExt.dll-t, mint például a Windows Intézője. E lépés nélkül a DLL-t nem lehet felülírni, így a Delphi-ben a fordításkor a Could not create output file hibaüzenettel találjuk szembe magunkat.
Nézzük most miként is készült ez a DLL, hogyan írhatunk tetszőleges, saját funkcióinkkal felszerelt alkalmazást.
A megvalósításhoz egy speciális DLL-re van szükségünk, mely COM objektumot tartalmaz. Ettől nem kell megijednie annak sem, aki esetleg ilyet még nem használt. A cikk végére belátható lesz, hogy ennek elkészítése sem ütközik különösebb nehézségekbe.
Hozzuk tehát létre a szükséges projectet. Ehhez válasszuk a File - New menüpontot. Majd a megjelenő ablakból az ActiveX lapon az ActiveX Library elemet. Ekkor létrejön a DLL kerete.
Most adjunk hozzá egy COM objektumot. Szintén File - New, majd az ActiveX lapon a COM Objects elem kiválasztása után megjelenik a COM objektum varázsló. Itt tudunk nevet adni az új objektumunknak a Class Name mezőben. A mellékelt példában a ShellMenu szöveget írtuk ide.
A példány és szálkezelés módját hagyjuk az alapértelmezett értékeken. A negyedik mezőben adhatjuk meg, hogy milyen osztályokból, vagyis interfészekből származzon az új objektumunk. A mellékelt példához az IShellExtInit, IContextMenu interfészeket kell használnunk, így ezeket vesszővel elválasztva felsoroljuk. Delphi 5, vagy későbbi változat esetén az Options-nál az Include Type Library-t kapcsoljuk ki.
Az utolsó, Description mezőbe egy egyedi leírást adhatunk meg, hogy mit is tud majdan e programunk. Ez persze elhagyható, nem kötelező.
Ezek után az Ok gomb lenyomásával létrejön az alábbi forráskód.
unit Unit1;
interface
uses Windows, ActiveX, Classes, ComObj;
type
TShellMenu = class(TComObject, IShellExtInit, IContextMenu)
protected
{Declare IShellExtInit methods here}
{Declare IContextMenu methods here}
end;
const Class_ShellMenu: TGUID =
'{51E5E6D3-929B-418C-A72A-E079B182048C}';
implementation
uses ComServ;
initialization
TComObjectFactory.Create(ComServer, TShellMenu,
Class_ShellMenu, 'ShellMenu', '', ciMultiInstance, tmApartment);
end.
Mint látható a ShellMenu néven megadott osztály automatikusan megkapta a T előtagot. Az osztály az általunk megadott két interfészből valamint a TComObject-ből származik. A helyes működés érdekében meg kell adnunk az IShellExtInit, IContextMenu interfészek metódusait:
TShellMenu = class(TComObject, IShellExtInit, IContextMenu)
protected
{Declare IShellExtInit methods here}
function IShellExtInit.Initialize=ShellInit;
function ShellInit(pidlFolder: PItemIDList; lpdobj:
IDataObject; hKeyProgID: HKEY): HResult; stdcall;
{Declare IContextMenu methods here}
function QueryContextMenu(Menu: HMENU; indexMenu,
idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;
function InvokeCommand(var lpici: TCMInvokeCommandInfo):
HResult; stdcall;
function GetCommandString(idCmd, uType: UINT;
pwReserved: PUINT; pszName: LPSTR; cchMax: UINT):
HResult; stdcall;
end;
Mivel az IShellExtInit interfész tartalmaz egy Initialize metódust, valamint a TComObject osztály is, így az IShellExtInit Initialize metódusának új nevet kell adnunk, ami most ShellInit lesz.
function IShellExtInit.Initialize=ShellInit;
function ShellInit(pidlFolder: PItemIDList; lpdobj:
IDataObject; hKeyProgID: HKEY): HResult; stdcall;
Miután deklaráltuk az interfészek metódusait, most már csak a megfelelő kódot kell hozzárendelnünk.
Amint az a két interfész nevéből már sejthető, az IShellExtInit egyetlen Initialize nevű függvénye a Shell kiterjesztés inicializálásához kell. A másik IContextMenu interfész pedig a jobb gomb lenyomásakor megjelenő menürendszer kezelésére szolgál.
Nézzük az IShellExtInit Initialize nevű függvényét, melyet átneveztünk ShellInit-re.
Amikor ez a függvény meghívásra kerül, akkor tudjuk meghatározni, hogy a Windows Intézőjében mely állományok lettek kijelölve. Mellékelt példában csak egyetlen kijelölt állomány kezelését oldottuk meg. Ez persze kiterjeszthető könnyedén tetszőleges számú állomány kezelésére is. Ennek az egy állománynak a nevét a FFileName globális változóba tároljuk el. Ezt a lekérdezést az alábbi forráskóddal valósítjuk meg.
function TShellMenu.ShellInit(pidlFolder: PItemIDList;
lpdobj: IDataObject; hKeyProgID: HKEY): HResult;
var
sm: TStgMedium;
fe: TFormatEtc;
count: integer;
a: array[0..MAX_PATH-1] of char;
begin
result:=E_FAIL;
if lpdobj<>nil then begin
with fe do begin
cfFormat:=CF_HDROP;
ptd:=nil;
dwAspect:=DVASPECT_CONTENT;
lindex:=-1;
tymed:=TYMED_HGLOBAL;
end;
if lpdobj.GetData(fe, sm)=S_OK then begin
try
count:=DragQueryFile(sm.hGlobal, $FFFFFFFF, nil, 0);
if count=1 then begin
DragQueryFile(sm.hGlobal, 0, a, MAX_PATH);
FFileName:=a;
result:=NOERROR;
end;
finally
ReleaseStgMedium(sm);
end;
end;
end;
end;
A count:=DragQueryFile(sm.hGlobal, $FFFFFFFF, nil, 0); sor a count változóba kérdezi le, hogy hány kijelölt állományról van szó.
Ezek után a DragQueryFile(sm.hGlobal, 0, a, MAX_PATH); hívásával kérdezhetjük le, hogy mi az állomány neve teljes elérési útvonallal. Itt a második paraméterben adhatjuk meg, hogy hányadik kijelölt állomány nevet szeretnénk lekérdezni. Ha tehát az összes névre kíváncsiak vagyunk, akkor írhatunk egy ciklust, mely nullától az előbbi count-1 értékig mehet. A ciklusváltozót a második paraméterben megadva a ciklus végéig az összes nevet megkapjuk.
Mivel a mi példánkban csak egy állomány nevet kezelünk, így nincs szükségünk ciklusra.
Tehát amikor a felhasználó jobb gombbal kattint egy állományon, akkor kerül meghívásra a DLL-ünk ShellInit függvénye, ahol meghatározható, hogy melyik is ez az állomány.
Nézzük most, hogy miként adhatunk hozzá menüpontot a megjelenő menürendszerhez.
Az IContextMenu interfész QueryContextMenu függvénye kerül meghívásra elsőként. Itt kell rendelkeznünk arról, hogy létrehozunk-e menüpontokat vagy sem. Mellékelt példánkban egy új menüpontot szúrunk be a menürendszerbe méghozzá úgy, hogy a miénk legyen az első.
function TShellMenu.QueryContextMenu(Menu: HMENU;
indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult;
begin
InsertMenu(Menu, indexMenu, MF_STRING+
MF_BYPOSITION, idCmdFirst, PChar('1. '+FFileName));
result:=1;
end;
Ehhez az InsertMenu Windows függvényt használjuk. Első paraméterként a menü azonosítóját kell megadnunk. Ezt szerencsére a QueryContextMenu függvény első paraméterében megkapjuk, így csak tovább kell adnunk. A második paraméterben határozhatjuk meg az új menüpont helyét. Ezután azt határozzuk meg a konstansokkal, hogy sztring típusú menüpontot hozunk létre az általunk megadott helyen. Negyedik paraméterként a menühöz rendelt azonosító számot kell megadnunk. A későbbiekben e szám alapján azonosíthatjuk majd, hogy melyik menüpontunk lett kiválasztva. Az itt megadott érték a paraméterként kapott idCmdFirst, idCmdLast érték között kell hogy legyen. Végső paraméterként a menüpontunk szövegét kell megadnunk. Ez itt az 1. sztring és az állomány neve összevonva lesz.
A QueryContextMenu függvény visszatérési értékének azt a számot kell adnunk, ahány menüpontot felvettünk a menürendszerbe. Ez jelen esetben egy.
Amikor a felhasználó megjelenítette az Intézőben a jobb gombbal ezt a menürendszert és az egérrel az egyes menüpontok felett mozog, akkor az Intéző alsó státusz sorába egy tetszőleges súgó szöveget jeleníthetünk meg. Amikor a mi menüpontunk fölé áll a felhasználó az egérrel, akkor kerül meghívásra a GetCommandString függvény.
function TShellMenu.GetCommandString(idCmd, uType: UINT;
pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
begin
result:=NOERROR;
if (idCmd=0) and (uType=GCS_HELPTEXT) then begin
StrCopy(pszName, 'Animare Software példaprogram
© 2000 (http://www.animare.hu)');
end;
end;
Itt az idCmd paraméterben kapjuk meg, hogy mi a menüpontunk azonosító kódja (hiszen több menüpont is lehet a sajátunk) és a pszName változóba kell másolnunk a súgó szövegünket, mely maximum cchMax karaktert tartalmazhat.
A végső kérdés pedig az lesz, hogy mi történik akkor, ha a felhasználó nem csak nézegeti, hanem ki is választja a menüpontunkat. Nos ekkor kerül meghívásra az InvokeCommand.
function TShellMenu.InvokeCommand(
var lpici: TCMInvokeCommandInfo): HResult;
var
i: integer;
begin
result:=E_FAIL;
if HiWord(integer(lpici.lpVerb))=0 then begin
i:=LOWORD(lpici.lpVerb);
if i=0 then begin
ShowMessage(TimeToStr(Now));
result:=NOERROR;
end;
end;
end;
A mellékelt példa egyszerűségének kedvéért itt most csak megjelenítünk egy kis üzenet ablakot benne az aktuális időponttal. Ha több menüpontunk lenne, akkor a programunkat az i változó értékétől függően ágaztathatjuk szét.
Ezzel a programunk el is készült, a DLL-t lefordíthatjuk. De honnan fogja tudni a Windows Intézője, hogy mikor kell meghívni a DLL-ünket és az hol található? Ehhez van szükségünk arra, hogy a Windows regisztrációs állományába elhelyezzük a szükséges bejegyzéseket.
Ezek a bejegyzések a ShellMenuExt.reg állományban láthatók. Ezt az állományt egyszerűen hozzáadhatjuk a regisztrációs adatbázishoz, ahogyan ezt a cikk elején le is írtuk. Természetesen lehetőség van arra, hogy ezeket a bejegyzéseket programból is beírhassuk a regisztrációs adatbázisba. Ehhez használhatjuk a TRegistry osztályt.
Nézzük milyen értékekre van szükség.
Általában egy ilyen lehetőséget csak egy adott állomány típus esetén használunk. Mellékelt példában ez az állomány SEC kiterjesztéssel rendelkezik. Vagyis a DLL-ünk csak akkor kap szerepet, ha a felhasználó SEC kiterjesztésű állományon kattint jobb gombbal.
A regisztrációs adatbázis HKEY_CLASSES_ROOT főkulcsa alá létre kell hoznunk egy .SEC kulcsot. Itt az alapértelmezett értéknek a SECFile sztringet adjuk. A későbbiekben ezzel a sztringgel hivatkozunk majd a bejegyzésre.
[HKEY_CLASSES_ROOT\.sec]
@="SECFile"
Szintén a HKEY_CLASSES_ROOT alá be kell jegyeznünk az imént megadott sztringet a SECFile-t. Itt az alapértelmezett értéknek egy tetszőleges sztringet adhatunk, mely leírja, hogy mi ez az állomány.
[HKEY_CLASSES_ROOT\SECFile]
@="DSO Sample file"
Ehhez a kulcshoz kell bejegyeznünk egy olyan alkulcsot, melynek neve Shellex\ContextMenuHandlers. Ebből fogja tudni a Windows, hogy a SEC állományhoz tartozik Shell kiegészítés, mégpedig egy menükezelő alkalmazás. Az alapértelmezett értéknek itt is egy tetszőleges sztringet adhatunk, melyet most SECMenu-re választottunk.
[HKEY_CLASSES_ROOT\SECFile\Shellex\ContextMenuHandlers]
@="SECMenu"
Minden COM objektum rendelkezik egyedi azonosítóval, melyet GUID-nak neveznek. Amikor létrehoztuk a programban a COM objektumunkat, akkor ezt a számot a Delphi automatikusan generálta és egy konstans formájában elhelyezte a forráskódunkban is.
const
Class_ShellMenu: TGUID =
'{8D67BEE0-F82A-4B7D-B430-2D3472331A45}';
Ezt az azonosító számot is meg kell adnunk a regisztrációs adatbázisban a következő kulcs alá:
[HKEY_CLASSES_ROOT\SECFile\shellex\ContextMenuHandlers\SECMenu]
@="{8D67BEE0-F82A-4B7D-B430-2D3472331A45}"
Amint az látható is az új kulcs neve egyezik az előbb szabadon megválasztott sztringünkkel, a SECMenu-vel. Itt alapértelmezett értékként a forráskódban lévő GUID értékét kell megadnunk. Legegyszerűbb, ha ezt átmásoljuk onnan.
A továbbiakban meg kell adnunk, hogy ehhez a GUID-hoz melyik alkalmazás tartozik. Ehhez a HKEY_CLASSES_ROOT főkulcs alá a CLSID kulcshoz be kell jegyeznünk a mi alkalmazásunkat is. A CLSID alatt található az összes COM objektum, mely regisztrált az adott számítógépen. Itt hozzunk létre egy új kulcsot, melynek neve egyezik a GUID értékével. Alapértelmezett értékként egy egyedi sztringet adhatunk, mely most a ShellMenuExt Sample szöveget kapta.
[HKEY_CLASSES_ROOT\CLSID\
{8D67BEE0-F82A-4B7D-B430-2D3472331A45}]
@="ShellMenuExt Sample"
E kulcs alá kell létrehoznunk egy InProcServer32 kulcsot, ahol megadható a DLL neve, elérési útvonala.
[HKEY_CLASSES_ROOT\CLSID\
{8D67BEE0-F82A-4B7D-B430-2D3472331A45}\InProcServer32]
@="D:\\Dso\\0245\\ShellSecret01\\ShellMenuExt.dll"
"ThreadingModel"="Apartment"
Amint az látható, a DLL-t elérési útvonallal az alapértelmezett értékhez kell megadni. Ha .REG állományt használunk, akkor itt a \ jelet duplán kell használnunk. Ha például Delphi-s programból végeznénk a regisztrációt, akkor a TRegistry használatával egy egyszerű sztringként írhatjuk ide az értéket. Ekkor nem kell duplázni e karaktert.
Továbbá meg kell adnunk még a ThreadingModel értéket is, mely a szálkezelés módját írja le, ami Apartment kell hogy legyen.
Végül már csak egyetlen bejegyzés maradt: a HKEY_LOCAL_MACHINE főkulcs alá a Microsoft\Windows\CurrentVersion\Shell Extensions\Approved kulcsra be kell jegyeznünk alkalmazásunkat. Itt létre kell hoznunk egy értéket, mely egyezik a GUID értékével és ennek azt a sztringet kell értékül adnunk, mely egyezik az imént még szabadon választott sztringünkkel.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
Windows\CurrentVersion\Shell Extensions\Approved]
"{8D67BEE0-F82A-4B7D-B430-2D3472331A45}"=
"ShellMenuExt Sample"
A regisztrációs adatbázis változtatása után az alkalmazásunkat már megtalálja a Windows, így ha egy .SEC kiterjesztésű állományra kattintunk, akkor az elkészített ShellMenuExt.dll kerül meghívásra.
|
Könyv
Ez a cikk megtalálható ebben a könyvben:
Delphi Software Offline 2000 évkönyv 459. oldal
Felhasználási feltételek
A Software Online szoftverfejlesztői magazin mindegyik cikke, minden megjelent képe, és egyéb publikált anyaga szerzői jog védelme alatt áll! Bármilyen formában történő másodlagos terjesztésük, közzétételük vagy felhasználásuk kizárólag a kiadó előzetes írásbeli engedélyével történhet!
|