Author: William Gerbert
Does anybody know how to write a Delphi program that can add itself to the Windows
Explorer right-click menu? I have seen some simple cases like adding NotePad for
txt files but that only works on one file (if you highlight many files then many
instances of Notepad will be created). I want to be able to highlight a group of
files and then pass all of them (probably through a command line argument) to my
progam so it can act on the group of them.
Answer:
Implement IContextMenu and IShellExtInit:
1
2 TOFCContextMenu = class(TComObject, IContextMenu, IShellExtInit)
3 private
4 FileList: TStringList;
5 protected
6 function IShellExtInit.Initialize = IShellExtInit_Initialize;
7 function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast,
8 uFlags: UINT): HResult; stdcall;
9 function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
10 function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR;
11 cchMax: UINT): HResult; stdcall;
12 function IShellExtInit_Initialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
13 hKeyProgID: HKEY): HResult; stdcall;
14 public
15 destructor Destroy; override;
16 end;
17
18 {In the Initialize method of the IShellExtInit interface you can determine which
19 files are selected:}
20
21 function TOFCContextMenu.IShellExtInit_Initialize(pidlFolder: PItemIDList;
22 lpdobj: IDataObject; hKeyProgID: HKEY): HResult; stdcall;
23 var
24 StgMedium: TStgMedium;
25 FormatEtc: TFormatEtc;
26 szFile: array[0..MAX_PATH + 1] of Char;
27 FileCount: Integer;
28 FileCounter: Integer;
29 begin
30 try
31 if (lpdobj <> nil) then
32 begin
33 with FormatEtc do
34 begin
35 cfFormat := CF_HDROP;
36 ptd := nil;
37 dwAspect := DVASPECT_CONTENT;
38 lindex := -1;
39 tymed := TYMED_HGLOBAL;
40 end;
41 Result := lpdobj.GetData(FormatEtc, StgMedium);
42 if (not Failed(Result)) then
43 begin
44 FileList := TStringList.Create;
45 FileList.Clear;
46 FileList.Sorted := True;
47 FileList.Duplicates := dupIgnore;
48 FileCount := DragQueryFile(stgmedium.hGlobal, $FFFFFFFF, nil, 0);
49 for FileCounter := 0 to FileCount - 1 do
50 begin
51 DragQueryFile(stgmedium.hGlobal, FileCounter, szFile, SizeOf(szFile));
52 FileList.Add(StrPas(szFile));
53 end;
54 Result := NOERROR;
55 ReleaseStgMedium(StgMedium);
56 end;
57 end
58 else
59 Result := E_INVALIDARG;
60 except
61 Result := E_FAIL;
62 end;
63 end;
64
65 //The file list must be freed in the destructor:
66
67 destructor TOFCContextMenu.Destroy;
68 begin
69 try
70 FileList.Free;
71 except
72 end;
73 inherited Destroy;
74 end;
Now implement the other methods:
QueryContextMenu
InvokeCommand
GetCommandString
At the end of the unit you can register the extension:
75
76 initialization
77 TRegisterContextMenuFactory.Create(ComServer, TOFCContextMenu,
78 Class_OFCContextMenu,
79 'OFCContextMenu', 'A description', ciMultiInstance, tmApartment);
Remember to protect every method with try..except or try..finally. The main
application is the explorer. It doesn't support exception handling like a delphi
application does. An exception outside a try..except/finally compound causes the
explorer to crash.
The TRegisterContextMenuFactory object looks something like this:
80 type
81 TRegisterContextMenuFactory = class(TComObjectFactory)
82 protected
83 function GetProgID: string; override;
84 public
85 procedure UpdateRegistry(register: Boolean); override;
86 end;
87
88 function TRegisterContextMenuFactory.GetProgID: string;
89 begin
90 Result := '';
91 end;
92
93 procedure TRegisterContextMenuFactory.UpdateRegistry(register: Boolean);
94 const
95 ApproveKey =
96 'SOFTWARE\Microsoft\Windows\CurrentVersion\ShellExtensions\Approved\';
97 var
98 ClsID: string;
99 begin
100 inherited UpdateRegistry(register);
101 ClsID := GUIDToString(ClassID);
102 if (register) then
103 try
104 {Additional registry settings }
105 if Win32Platform = VER_PLATFORM_WIN32_NT then
106 CreateRegKeyEx(ApproveKey, ClsId, PChar(Description), REG_SZ,
107 Length(Description) + 1, HKEY_LOCAL_MACHINE);
108 except
109 end
110 else
111 try
112 if Win32Platform = VER_PLATFORM_WIN32_NT then
113 DeleteRegValue(ApproveKey, ClsId, HKEY_LOCAL_MACHINE);
114 {Delete additional registry settings }
115 except
116 end;
117 end;
Instead of {Additional registry settings } you must add the registry keys for the extension. Like which file exctension is associated. You can use HKEY_LOCAL_MACHINE\* for all extensions.
|