Using Delphi to write Windows shell extensions
Friends who are familiar with the principles of operating systems will know that a complete operating system will provide a shell to facilitate ordinary users.
Use various functions provided by the operating system. The shell of Windows (here refers to Windows 95, Windows NT4.0 or above operating system) not only provides
It not only provides a convenient and beautiful GUI graphical interface, but also provides powerful shell extension functions. You may see these shell extensions in many software. For example in your
If Winzip is installed in the system, when you right-click a folder or file in Windows Explorer, the Winzip file will appear in the pop-up menu.
abbreviated menu. Or the FTP site folder that appears in Windows Explorer in Bullet FTP.
Windows supports seven types of shell extensions (called Handlers), and their corresponding functions are briefly described as follows:
(1)Context menu handlers: Add context-sensitive menus to specific types of file objects;
(2) Drag-and-drop handlers are used to support OLE data transmission when users perform drag-and-drop operations on certain types of file objects;
(3) Icon handlers are used to provide a unique icon to a certain file object, and can also specify an icon for a certain type of file object;
(4) PRoperty sheet handlers add property pages to file objects (that is, right-click the file object or folder object and select Properties in the pop-up menu
dialog box that appears after the item), property pages can be shared by file objects of the same type, or a unique property page can be assigned to a file object;
(5) Copy-hook handlers will be called by the system when the folder object or printer object is copied, moved, deleted, or renamed, through Windows
Add Copy-hook handlers to allow or prohibit certain operations;
(6) Drop target handlers will be called by the system when an object is dragged and dropped onto another object;
(7)Data object handlers will be called by the system when files are dragged, dropped, copied or pasted.
All shell extensions of Windows are based on the COM (Component Object Model) component model, and the shell accesses objects through interfaces.
Shell extensions are designed as 32-bit in-process server programs and provide services to the operating system in the form of dynamic link libraries. Therefore, if you want to use Windows
If the user interface is expanded, it is very necessary to have some knowledge of writing COM objects. Due to space limitations, COM will not be introduced here. Readers can refer to
According to Microsoft's MSDN library or related help documents, an interface can be regarded as a special class, which contains a set of functions and procedures that can be used to operate an object.
After you write your shell extensions, you must register them to take effect. All shell extensions must be in the Windows registry under the HKEY_CLASSES_ROOTCLSID key
Register below. Under this key, you can find many keys with names like {0000002F-0000-0000-C000-000000000046}. Such keys are globally unique class identifiers.
Guid. Every shell extension must have a globally unique class identifier, which is how Windows finds the shell extension handler.
The location of the shell extension dynamic link library in the system is recorded under the InProcServer32 subkey under the class identifier. Shell extensions associated with a file type are registered in
Under the shellex primary key of the corresponding type. If the Windows operating system is Windows NT, the shell extension must also be in the registry.
Register under the primary key HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsCurrentVersionShellExtensionsApproved.
After compiling the shell extension DLL program, you can use regsvr32.exe provided by Windows itself to register the DLL server program. If using Delphi, you can also
To register, select Register ActiveX Server in the Run menu.
Let's first introduce a more commonly used shell extension application: the context-sensitive menu, which pops up when you right-click a file or folder in Windows.
This menu is called a context-sensitive menu. To dynamically add menu items to a context-sensitive menu, you can write a Context Menu Handler. For example, everyone
Familiar software such as WinZip and UltraEdit dynamically add menu items to the menu by writing a Context Menu Handler. If installed in the system
WinZip, then when you right-click a file (folder) named Windows, its context-sensitive menu will have a menu item named Add to Windows.zip.
The Context Menu Handler to be implemented in this article is similar to the context menu provided by WinZip. It will add a
File operation menu item. When this item is clicked, the interface program will pop up a file operation window to perform operations such as file copying and moving.
Writing Context Menu Handler must implement three interfaces: IShellExtInit, IContextMenu and TComObjectFactory. IShellExtInit implementation
Initialization of the interface, the IContextMenu interface object implements the context-sensitive menu, and the IComObjectFactory interface implements the creation of the object.
The specific program implementation is introduced below. First, click the File|New item of the menu in Delphi, select DLL in the New Item window to create a DLL project file.
Then click the File|New item of the menu, select Unit in the New Item window to create a Unit file, click the File|New item of the menu, and select Unit in the New Item window.
Select Form to create a new window. Save the project file as Contextmenu.dpr, Unit1 as Contextmenuhandle.pas, and Save the Form as
OpWindow.pas.
The program list of Contextmenu.dpr is as follows:
library contextmenu;
uses
ComServ,
contextmenuhandle in 'contextmenuhandle.pas',
opwindow in 'opwindow.pas' {Form2};
exports
DllGetClassObject,
DllCanUnloadNow,
DllRegisterServer,
DllUnregisterServer;
{$R *.TLB}
{$R *.RES}
begin
end.
The program list of Contextmenuhandle is as follows:
unit ContextMenuHandle;
interface
uses Windows,ActiveX,ComObj,ShlObj,Classes;
type
TContextMenu = class(TComObject,IShellExtInit,IContextMenu)
private
FFileName: array[0..MAX_PATH] of Char;
protected
function IShellExtInit.Initialize = SEIInitialize; // Avoid compiler warning
function SEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
hKeyProgID: HKEY): HResult; stdcall;
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;
const
Class_ContextMenu: TGUID = '{19741013-C829-11D1-8233-0020AF3E97A0}';
{Globally Unique Identifier (GUID) is a 16-byte (128-byte) value that uniquely identifies an interface}
var
FileList:TStringList;
implementation
uses ComServ, SysUtils, ShellApi, Registry,UnitForm;
function TContextMenu.SEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
hKeyProgID: HKEY): HResult;
var
StgMedium: TStgMedium;
FormatEtc: TFormatEtc;
FileNumber,i:Integer;
begin
file://If lpdobj equals Nil, this call fails
if (lpdobj = nil) then begin
Result := E_INVALIDARG;
Exit;
end;
file:// first initializes and clears FileList to add files
FileList:=TStringList.Create;
FileList.Clear;
file://Initialize clipboard format file
with FormatEtc do begin
cfFormat := CF_HDROP;
ptd := nil;
dwaspect := DVASPECT_CONTENT;
lindex := -1;
tymed := TYMED_HGLOBAL;
end;
Result := lpdobj.GetData(FormatEtc, StgMedium);
if Failed(Result) then Exit;
file://First query the number of files selected by the user
FileNumber := DragQueryFile(StgMedium.hGlobal,$FFFFFFFF,nil,0);
file:// reads in a loop and saves all user-selected files to FileList
for i:=0 to FileNumber-1 do begin
DragQueryFile(StgMedium.hGlobal, i, FFileName, SizeOf(FFileName));
FileList.Add(FFileName);
Result := NOERROR;
end;
ReleaseStgMedium(StgMedium);
end;
function TContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
idCmdLast, uFlags: UINT): HResult;
begin
Result := 0;
if ((uFlags and $0000000F) = CMF_NORMAL) or
((uFlags and CMF_EXPLORE) <> 0) then begin
//Add a menu item to the Context Menu, the title of the menu item is View Bitmap File
InsertMenu(Menu, indexMenu, MF_STRING or MF_BYPOSITION, idCmdFirst,
PChar('file operation'));
// Return the number of added menu items
Result := 1;
end;
end;
function TContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
var
frmOP:TForm1;
begin
// First make sure the process is called by the system rather than by a program
if (HiWord(Integer(lpici.lpVerb)) <> 0) then
begin
Result := E_FAIL;
Exit;
end;
// Determine the validity of the passed parameters
if (LoWord(lpici.lpVerb) <> 0) then begin
Result := E_INVALIDARG;
Exit;
end;
file://Create a file operation window
frmOP:=TForm1.Create(nil);
file://adds a list of all files to the list in the file operation window
frmOP.ListBox1.Items := FileList;
Result := NOERROR;
end;
function TContextMenu.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
pszName: LPSTR; cchMax: UINT): HRESULT;
begin
if (idCmd = 0) then begin
if (uType = GCS_HELPTEXT) then
{Returns the help information of this menu item. This help information will be displayed when the user moves the mouse
Appears on the status bar when moving to this menu item. }
StrCopy(pszName, PChar('Clicking this menu item will perform file operations'));
Result := NOERROR;
end
else
Result := E_INVALIDARG;
end;
type
TContextMenuFactory = class(TComObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
procedure TContextMenuFactory.UpdateRegistry(Register: Boolean);
var
ClassID: string;
begin
if Register then begin
inherited UpdateRegistry(Register);
ClassID := GUIDToString(Class_ContextMenu);
file://When registering an extension library file, add the library to the registry
CreateRegKey('*shellex', ', ');
CreateRegKey('*shellexContextMenuHandlers', ', ');
CreateRegKey('*shellexContextMenuHandlersFileOpreation', ', ClassID);
file://if the operating system is Windows NT
if (Win32Platform = VER_PLATFORM_WIN32_NT) then
with TRegistry.Create do
try
RootKey := HKEY_LOCAL_MACHINE;
OpenKey('SOFTWAREMicrosoftWindowsCurrentVersionShell Extensions', True);
OpenKey('Approved', True);
WriteString(ClassID, 'Context Menu Shell Extension');
finally
Free;
end;
end
else begin
DeleteRegKey('*shellexContextMenuHandlersFileOpreation');
inherited UpdateRegistry(Register);
end;
end;
initialization
TContextMenuFactory.Create(ComServer, TContextMenu, Class_ContextMenu,
', 'Context Menu Shell Extension', ciMultiInstance,tmApartment);
end.
Add a TListBox control and two TButton controls to the OpWindow window. The program list of OpWindows.pas is as follows:
unit opwindow;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls,shlobj,shellapi,ActiveX;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{Private declarations}
public
FileList:TStringList;
{Public declarations}
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
FileList:=TStringList.Create;
Button1.Caption :='Copy file';
Button2.Caption :='Move files';
Self.Show;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FileList.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
sPath:string;
fsTemp:SHFILEOPSTRUCT;
i:integer;
begin
sPath:=InputBox('File operation','Input copy path','c:windows');
if sPath<>'then begin
fsTemp.Wnd := Self.Handle;
file://set file operation type
fsTemp.wFunc :=FO_COPY;
file:// allows undo operations
fsTemp.fFlags :=FOF_ALLOWUNDO;
for i:=0 to ListBox1.Items.Count-1 do begin
file://full pathname of source file
fsTemp.pFrom := PChar(ListBox1.Items.Strings[i]);
file://path to copy to
fsTemp.pTo := PChar(sPath);
fsTemp.lpszProgressTitle:='Copy file';
if SHFileOperation(fsTemp)<>0 then
ShowMessage('File copy failed');
end;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
sPath: string;
fsTemp:SHFILEOPSTRUCT;
i:integer;
begin
sPath:=InputBox('File operation','Input movement path','c:windows');
if sPath<>'then begin
fsTemp.Wnd := Self.Handle;
fsTemp.wFunc :=FO_MOVE;
fsTemp.fFlags :=FOF_ALLOWUNDO;
for i:=0 to ListBox1.Items.Count-1 do begin
fsTemp.pFrom := PChar(ListBox1.Items.Strings[i]);
fsTemp.pTo := PChar(sPath);
fsTemp.lpszProgressTitle:='Move files';
if SHFileOperation(fsTemp)<>0 then
ShowMessage('File copy failed');
end;
end;
end;
end.
Click the Project | Build ContextMenu item in the menu, and Delphi will create the Contextmenu.dll file. This is the context-sensitive menu program.
Use Regsvr32.exe to register the program, then right-click on any one or several files in Windows Explorer, and it will appear in the context menu.
There is an additional menu item for file operations. Click this item and the file names of all the files you selected will be listed in the list in the pop-up window. You can choose the copy file button or
The Move File button performs file operations.