📄 softlinkdragdrophandler.pas
字号:
//
// Robert Rossmair, 2001, 2004
//
// Adds "create junction here" entry to explorer context menu, when a directory
// is dragged & dropped onto a NTFS volume. When selected, it creates a NTFS
// junction to the source directory, instead of copying it to the new location.
//
// The name of the junction is prefixed with a "~" to mark it as different from
// a normal directory, since dumb ol' Explorer doesn't know nothing about NTFS
// junctions.
//
// This unit is based on $(DELPHI)\Demos\ActiveX\ShellExt\ContextM
//
unit SoftLinkDragDropHandler;
interface
uses
Windows, ActiveX, ComObj, ShlObj,
JclBase, JclStrings, JclFileUtils, JclShell, JclNTFS;
type
TDirDropContextMenu = class(TComObject, IShellExtInit, IContextMenu)
private
FLinkTarget: string;
FLinkPath: string;
FIsRootDirectory: Boolean;
protected
{ IShellExtInit }
function IShellExtInit.Initialize = SEIInitialize; // Avoid compiler warning
function SEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
hKeyProgID: HKEY): HResult; stdcall;
{ 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;
end;
const
Class_ContextMenu: TGUID = '{DDE0E099-9901-4507-9A47-3DC66B13AB6B}';
implementation
uses ComServ, SysUtils, ShellApi, Registry;
resourcestring
SDescription = 'JEDI SoftLinks Shell Extension';
SRegKeyDir = 'Directory\shellex\DragDropHandlers\JEDISoftLinks';
SRegKeyDrive = 'Drive\shellex\DragDropHandlers\JEDISoftLinks';
SMenuItem = 'Create junction here';
SMenuHelp = 'Create an NTFS junction point';
const
Prefix = '~';
function OnNtfsVolume(const FileName: string): Boolean;
begin
Result := NtfsReparsePointsSupported(ExtractFileDrive(FileName));
end;
function TDirDropContextMenu.SEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
hKeyProgID: HKEY): HResult;
var
FileName: string;
LinkDir: string;
Volume: string;
StgMedium: TStgMedium;
FormatEtc: TFormatEtc;
Count, N: Integer;
begin
FLinkPath := '';
if (lpdobj = nil) then
begin
Result := E_INVALIDARG;
Exit;
end;
with FormatEtc do
begin
cfFormat := CF_HDROP;
ptd := nil;
dwAspect := DVASPECT_CONTENT;
lindex := -1;
tymed := TYMED_HGLOBAL;
end;
// Render the data referenced by the IDataObject pointer to an HGLOBAL
// storage medium in CF_HDROP format.
Result := lpdobj.GetData(FormatEtc, StgMedium);
if Failed(Result) then
Exit;
// If only one file is selected, retrieve the file name and store it in
// FLinkTarget. Otherwise fail the call.
Count := DragQueryFile(StgMedium.hGlobal, $FFFFFFFF, nil, 0);
Result := E_FAIL;
if Count = 1 then
begin
SetLength(FLinkTarget, DragQueryFile(StgMedium.hGlobal, 0, nil, 0) + 1);
DragQueryFile(StgMedium.hGlobal, 0, PChar(FLinkTarget), Length(FLinkTarget));
if DirectoryExists(FLinkTarget) then
begin
LinkDir := PidlToPath(pidlFolder);
if OnNtfsVolume(LinkDir) then
begin
FileName := ExtractFileName(FLinkTarget);
StrResetLength(FileName);
FIsRootDirectory := FileName = '';
if FIsRootDirectory then
begin
Volume := ExtractFileDrive(FLinkTarget);
N := Pos(':', Volume);
if N > 0 then
SetLength(Volume, N - 1);
FileName := Volume;
end;
FLinkPath := Format('%s' + Prefix + '%.175s', [PathAddSeparator(LinkDir), FileName]);
Result := NOERROR;
end;
end;
end;
ReleaseStgMedium(StgMedium);
end;
function TDirDropContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
idCmdLast, uFlags: UINT): HResult;
begin
Result := 0; // or use MakeResult(SEVERITY_SUCCESS, FACILITY_NULL, 0);
if FLinkPath = '' then
Exit;
if ((uFlags and $0000000F) = CMF_NORMAL) or
((uFlags and CMF_EXPLORE) <> 0) then
begin
// Add one menu item to context menu
InsertMenu(Menu, indexMenu, MF_STRING or MF_BYPOSITION, idCmdFirst, PChar(SMenuItem));
// Return number of menu items added
Result := 1; // or use MakeResult(SEVERITY_SUCCESS, FACILITY_NULL, 1)
end;
end;
function TDirDropContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
var
Success: Boolean;
begin
Result := E_FAIL;
if (HiWord(Integer(lpici.lpVerb)) <> 0) then
begin
// We are called by an application
Exit;
end;
if (LoWord(lpici.lpVerb) <> 0) then
begin
// invalid argument number
Result := E_INVALIDARG;
Exit;
end;
if (not DirectoryExists(FLinkPath) and CreateDir(FLinkPath)) {or DirectoryIsEmpty(FLinkPath)} then
begin
Success := NtfsCreateJunctionPoint(FLinkPath, FLinkTarget);
if Success then
SHChangeNotify(SHCNE_MKDIR, SHCNF_PATH, PChar(FLinkPath), nil);
end;
end;
function TDirDropContextMenu.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
pszName: LPSTR; cchMax: UINT): HRESULT;
begin
if (idCmd = 0) then
begin
if (uType = GCS_HELPTEXT) then
// return help string for menu item
StrCopy(pszName, PChar(SMenuHelp));
Result := NOERROR;
end
else
Result := E_INVALIDARG;
end;
type
TDirDropContextMenuFactory = class(TComObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
procedure TDirDropContextMenuFactory.UpdateRegistry(Register: Boolean);
var
ClassID: string;
begin
if Register then
begin
inherited UpdateRegistry(Register);
ClassID := GUIDToString(Class_ContextMenu);
CreateRegKey(SRegKeyDir, '', ClassID);
CreateRegKey(SRegKeyDrive, '', 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, SDescription);
finally
Free;
end;
end
else
begin
DeleteRegKey(SRegKeyDir);
DeleteRegKey(SRegKeyDrive);
inherited UpdateRegistry(Register);
end;
end;
initialization
TDirDropContextMenuFactory.Create(ComServer, TDirDropContextMenu, Class_ContextMenu,
'', SDescription, ciMultiInstance,
tmApartment);
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -