Developpez.com

Une très vaste base de connaissances en informatique avec
plus de 100 FAQ et 10 000 réponses à vos questions

Developpez.com - Delphi
X

Choisissez d'abord la catégorieensuite la rubrique :


Extension du shell - Menu contextuel

Date de publication : 24/02/2005

Par Pierre Castelain (Contributions)
 

Tutoriel sur l'ajout d'éléments dans le menu contextuel du shell de Windows en utilisant la technique des extensions du shell


I. Introduction
II. Principe
III. Mise en oeuvre


I. Introduction

Ce petit tutoriel est une reproduction d'une réponse faite directement dans un fil de discussion du forum Delphi de developpez.com. Il a pour but de montrer comment ajouter un ou plusieurs éléments de menu dans le menu contextuel du shell de Windows. Cette méthode est plus complexe que le simple ajout de commandes dans la ruche correspondant au type de fichier dans la base de registre, mais elle présente de nombreux avantages. En particulier la possibilité de tester les fichiers sélectionnés avant l'ouverture du menu ainsi que celle de modifier le texte de l'item de menu en fonction des fichiers sélectionnés.


II. Principe

Une extension du shell est un objet COM qui implémente certaines interfaces spécifiques (IShellExtInit et IContextMenu). Pour que cet objet soit utilisé par le shell, il faut bien sur que la dll qui le contient soit enregistrée (regsvr32 ou directement depuis Delphi par le menu Exécuter->Recenser le serveur ActiveX). De plus, il faut que l'extension soit également inscrite dans la base de registre (voir la méthode UpdateRegistry).

Lorsque l'utilisateur sélectionne des fichiers et fait un clic droit, le shell instancie un objet de notre classe et appelle les méthodes suivantes:

  • Initialize : qui permet d'obtenir le nom des fichiers sélectionnés (entre autres),
  • QueryContextMenu : qui permet à notre objet d'ajouter ces items de menu dans le menu contextuel,
  • GetCommandString : appelée pour obtenir une chaîne de description de notre extension,
  • InvokeCommand : quand l'utilisateur clique sur un de nos items.

III. Mise en oeuvre

Voila pour la théorie, maintenant un peu de pratique.

Il faut commencer par créer une bibliothèque ActiveX (menu Fichier->Nouveau->ActiveX). Nous allons l'appeler ShellExtTest.

Ensuite, il nous faut un objet COM (menu Fichier->Nouveau->ActiveX) que nous appellerons ShellExtTester (Delphi va automatiquement ajouter un I devant le nom de l'interface).

La nouvelle unité créée sera enregistrée sous le nom ShellExtTesterImpl.

Il ne reste plus qu'à recopier le code suivant (légèrement commenté):

unit ShellExtTesterImpl; interface uses Windows, ActiveX, Classes, ComObj, ShellExtTest_TLB, StdVcl, ShlObj; type TShellExtTester = class(TTypedComObject, IShellExtTester, IShellExtInit, IContextMenu) private FFilenames: array of PChar; // Tableau qui va contenir le nom des fichiers protected {Déclarez les méthodes IShellExtTester ici} { Méthode de l'interface IShellExtInit } { La méthode Initialize existe déja dans notre objet COM, il nous faut donc changer le nom de la méthode Initialize de IShellExtInit } function IShellExtInit.Initialize = ShellInit; function ShellInit(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult; stdcall; { Méthode de l'interface IContextMenu } 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; public destructor Destroy; override; end; TShellExtTesterFactory = class(TComObjectFactory) public procedure UpdateRegistry(Register: Boolean); override; end; implementation uses ComServ, ShellAPI, SysUtils, Registry, Dialogs; { TShellExtTester } destructor TShellExtTester.Destroy; { Destruction de notre objet } var i: integer; begin for i:=0 to Length(FFilenames) - 1 do StrDispose(FFilenames[i]); inherited; end; function TShellExtTester.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult; { Récupération de la chaîne d'aide (affichée dans les statusbar)} begin if uType = GCS_HELPTEXT then begin StrLCopy(pszName, 'Un test d''extension du shell', cchMax); result:= NOERROR; end else result:= E_INVALIDARG; end; function TShellExtTester.InvokeCommand( var lpici: TCMInvokeCommandInfo): HResult; { Méthode appelée si l'utilisateur a cliqué sur notre item de menu } var i: integer; s: string; begin { Vérifier que l'on n'est pas appelé par une application } if (HiWord(Integer(lpici.lpVerb)) <> 0) then begin result := E_FAIL; Exit; end; if Length(FFileNames) > 0 then begin s:= ''; for i:=0 to Length(FFilenames)-1 do s:= s + FFilenames[i] + #13#10; MessageDlg(s, mtInformation, [mbOK], 0); end; result := NOERROR; end; function TShellExtTester.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; { Méthode appelée par le shell pour ajouter notre item de menu } begin if (uFlags and CMF_DEFAULTONLY)=0 then begin InsertMenu(Menu, IndexMenu, MF_STRING or MF_BYPOSITION, idCmdFirst, 'Tester l''extension du shell'); result:= 1; end else result:= NOERROR; end; function TShellExtTester.ShellInit(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult; { C'est la première méthode appelée par le Shell après la création de l'instance de notre classe. } var StgMedium: TStgMedium; FormatEtc: TFormatEtc; filesnum, i: integer; filename: PChar; nameLength: UInt; begin { Si lpdobj est nil, retourner un code d'erreur } if lpdobj = nil then begin Result := E_INVALIDARG; Exit; end; { On prépare la structure } with FormatEtc do begin cfFormat := CF_HDROP; ptd := nil; dwAspect := DVASPECT_CONTENT; lindex := -1; tymed := TYMED_HGLOBAL; end; { Récupération des données contenues dans lpdobj } Result := lpdobj.GetData(FormatEtc, StgMedium); if Failed(Result) then Exit; try { Si on a au moins un fichier, stocker les noms dans le tableau } filesnum:= DragQueryFile(StgMedium.hGlobal, $FFFFFFFF, nil, 0); if filesnum > 0 then begin SetLength(FFileNames, filesnum); for i:= 0 to filesnum-1 do begin { On fait un premier tour pour avoir la longueur du nom } nameLength:= DragQueryFile(StgMedium.hGlobal, i, nil, 0) + 1; filename:= StrAlloc(nameLength); DragQueryFile(StgMedium.hGlobal, i, filename, nameLength); FFilenames[i]:= filename; Result := NOERROR; end; end else begin SetLength(FFileNames, 0); Result := E_FAIL; end; finally ReleaseStgMedium(StgMedium); end; end; procedure TShellExtTesterFactory.UpdateRegistry(Register: Boolean); var ClassID: string; DocumentName: string; begin if Register then begin inherited UpdateRegistry(Register); ClassID := GUIDToString(Class_ShellExtTester); with TRegistry.Create do try RootKey:= HKEY_CLASSES_ROOT; if OpenKey('.txt', false) then DocumentName:= ReadString(''); finally Free; end; CreateRegKey(DocumentName+'\shellex', '', ''); CreateRegKey(DocumentName+'\shellex\ContextMenuHandlers', '', ''); CreateRegKey(DocumentName+'\shellex\ContextMenuHandlers\ShellExtTester', '', ClassID); if (Win32Platform = VER_PLATFORM_WIN32_NT) then with TRegistry.Create do try RootKey := HKEY_LOCAL_MACHINE; OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions', True); OpenKey('Approved', True); WriteString(ClassID, 'ShellExtTester Context Menu Extension'); finally Free; end; end else begin DeleteRegKey(DocumentName+'\shellex\ContextMenuHandlers\ShellExtTester'); inherited UpdateRegistry(Register); end; end; initialization TShellExtTesterFactory.Create(ComServer, TShellExtTester, Class_ShellExtTester, 'ShellExtTester', 'Test d''extension du shell', ciMultiInstance, tmApartment); end.
Voila, c'est fini. Il ne reste plus qu'à compiler et à recenser le serveur (par le menu exécuter ou par RegScr32).

Attention, une fois recensé vous ne pourrez pas recompiler si le shell est ouvert, c'est à dire si une fenêtre de l'explorateur est ouverte.


Articles
Delphi 2005 : Découvrez le futur Delphi 2005
DirectX : Introduction à DirectX 9 en Delphi
Variables d'environnement : Présentation, description et utilisation des variables d'environnement sous Windows
Mailslots : Présentation des mailslots et de leur utilisation en Delphi pour la communication inter-processus
Projets complets avec sources
NumericalParser : Parser numérique en Delphi afin de transformer une chaîne de caractères en valeur flottante ou entière.
RegSearch : Composant de recherche dans la base de registre
CDAReader : Lecture des informations contenues dans les fichier CDA de Windows
ScreenSaverPreview : Composant d'affichage de l'aperçu des économiseurs d'écran de Windows
ScanResources : Programme d'exploration des ressources des programmes ou des dll d'un répertoire
ClipboardViewer : Démonstration de la détection des modifications et de l'affichage du contenu du presse-papier
Matrix : Tentative de reproduction en Delphi de l'animation bien connue du film Matrix
Sources et exemples
EMFTransform : Transformation (rotation, inversion, miroir) d'un metafile Windows en mémoire
DeleteKeyTree : Suppression récursive d'un clé de la base de registre
MultiStrings : Routines de gestion de tableaux de chaînes C
GetDllFilename : Pour récupérer le chemin d'une dll par son handle
Extension du shell : Exemple d'extension du menu contextuel du shell de Windows
TriStringGrid : Exemple de tri par colonne d'un composant TStringGrid à l'aide d'un algorithme de tri rapide (quick sort)
XPManifestCPL : Utilisation des contrôles XP dans une application du panneau de configuration (cpl)
Bouboules : Modélisation à l'aide du design pattern Observer
Divers
Diagramme ternaire : Un logiciel gratuit de tracé de diagramme ternaire


Copyright © Pierre Castelain. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Responsables bénévoles de la rubrique Delphi : Gilles Vasseur - Alcatîz -