MiTec/Common/MiTeC_Shell.pas

518 lines
15 KiB
ObjectPascal
Raw Permalink Normal View History

2024-01-02 00:01:59 +01:00
{*******************************************************}
{ MiTeC Common Routines }
{ Shell routines }
{ }
{ Copyright (c) 1997-2015 Michal Mutl }
{ }
{*******************************************************}
{$INCLUDE Compilers.inc}
unit MiTeC_Shell;
interface
uses {$IFDEF RAD9PLUS}
WinAPI.Windows, System.Win.Registry, System.SysUtils, System.Classes, Vcl.ComCtrls, WinApi.CommCtrl, Vcl.Menus;
{$ELSE}
Windows, Registry,SysUtils, Classes, ComCtrls, CommCtrl, Menus;
{$ENDIF}
type
TNewEntryType = (etNullFile, etFileName, etCommand);
TShellLinkRecord = record
Name,
Target,
Arguments,
WorkingDirectory,
IconFile: string;
IconIndex,
ShowCmd: Integer;
HotKey: Word;
end;
{$IFNDEF FPC}
function HotKeyToShortCut(AHotkey: Word): THotKey;
function ShortCutToHotKey(AHKeyCtrl: THotKey): Word;
{$ENDIF}
procedure RegisterFileType(Extension, Typename, Description, Icon, EXEName :string);
procedure UnRegisterFileType(Extension, Typename: string);
function GetShellExtension(Extension,Typename: string): string;
procedure AddCMAction(RegistryKey, ActionName, MenuCaption, Action: string);
procedure RemoveCMAction(RegistryKey, ActionName: string);
procedure AddCMNew(Extension, Params: string; EntryType: TNewEntryType);
procedure RemoveCMNew(Extension: string);
//procedure CreateLinkToFile(FileName: string; LinkPath: string; LinkCaption: string);
procedure CreateShortCut(const LinkName: string; ARecord: TShellLinkRecord);
function ResolveLink(const LinkFile: TFileName; var ARecord: TShellLinkRecord): HRESULT;
function CheckShellCM(const ASection, AName: string): boolean;
procedure AddShellCM(const ASection, AName, AEXEName: string);
procedure RemoveShellCM(const ASection, AName: string);
procedure ShowShellContextPopup(const AFilename: string; X,Y: integer; AHandle: HWND);
implementation
uses {$IFDEF RAD9PLUS}
System.Win.ComObj, WinAPI.ShlObj, WinAPI.ActiveX;
{$ELSE}
ComObj, ShlObj, ActiveX;
{$ENDIF}
function AnsiToWide(const s: AnsiString; codePage: Word = CP_ACP): WideString;
var
l: integer;
f: Cardinal;
begin
f:=MB_PRECOMPOSED;
if codepage=CP_UTF8 then
f:=0;
if s = '' then
Result:=''
else begin
l:=MultiByteToWideChar(codePage,f,PAnsiChar(@s[1]),-1,nil,0);
SetLength(Result,l-1);
if l>1 then
MultiByteToWideChar(CodePage,f,PAnsiChar(@s[1]),-1,PWideChar(@Result[1]),l-1);
end;
end;
{$IFNDEF FPC}
function HotKeyToShortCut(AHotkey: Word): THotKey;
var
Shift: TShiftState;
hlb: byte;
begin
Result:=THotkey.Create(nil);
hlb:=WordRec(AHotkey).hi;
Shift:=[];
if (hlb and HOTKEYF_SHIFT)<>0 then
Shift:=Shift+[ssShift];
if (hlb and HOTKEYF_CONTROL)<>0 then
Shift:=Shift+[ssCtrl];
if (hlb and HOTKEYF_ALT)<>0 then
Shift:=Shift+[ssAlt];
AHotKey:=AHotKey and $00FF;
Result.HotKey:=ShortCut(AHotKey,Shift);
end;
function ShortCutToHotKey(AHKeyCtrl: THotKey): Word;
var
Key: Word;
shb: Byte;
Shift: TShiftState;
begin
ShortCutToKey(AHKeyCtrl.HotKey, Key, Shift);
shb:=0;
if ssShift in Shift then
shb:=shb+HOTKEYF_SHIFT;
if ssCtrl in Shift then
shb:=shb+HOTKEYF_CONTROL;
if ssAlt in Shift then
shb:=shb+HOTKEYF_ALT;
Result:=(shb shl 8) or key;
end;
{$ENDIF}
procedure RegisterFileType(Extension, Typename, Description, Icon, EXEName :string);
var
p: integer;
begin
p:=pos('.',Extension);
while p>0 do begin
Delete(Extension,p,1);
p:=pos('.',Extension);
end;
if (Extension='') or (EXEName='') then
Exit;
Extension:='.'+Extension;
with TRegistry.Create do
try
RootKey:=HKEY_CURRENT_USER;
if OpenKey(Format('\Software\Classes\%s',[Extension]), true) then begin
WriteString('',Typename);
CloseKey;
end;
if OpenKey(Format('\Software\Classes\%s',[Typename]), true) then begin
WriteString('',Description);
CloseKey;
end;
if OpenKey(Format('\Software\Classes\%s\DefaultIcon',[Typename]), true) then begin
WriteString('',Icon);
CloseKey;
end;
if OpenKey(Format('\Software\Classes\%s\shell\open\command',[Typename]), true) then begin
WriteString('',Format('%s "%%1"',[ExeName]));
CloseKey;
end;
finally
Free;
end;
SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_IDLIST,nil,nil);
{with TRegINIFile.Create('') do
try
RootKey:=HKEY_CLASSES_ROOT;
if RegistryKey='' then
RegistryKey:=Copy(Extension,2,MaxInt)+'_auto_file';
WriteString(Extension,'',RegistryKey);
WriteString(RegistryKey,'',Description);
if Icon<>'' then
WriteString(RegistryKey+'\DefaultIcon','',Icon);
WriteString(RegistryKey+'\shell\open\command','',EXEName+' "%1"');
finally
Free;
end;}
end;
procedure UnRegisterFileType(Extension, Typename: string);
var
s: string;
p: integer;
begin
p:=pos('.',Extension);
while p>0 do begin
Delete(Extension,p,1);
p:=pos('.',Extension);
end;
if (Extension='') or (Typename='') then
Exit;
Extension:='.'+Extension;
with TRegistry.Create do
try
RootKey:=HKEY_CURRENT_USER;
if OpenKey(Format('\Software\Classes\%s',[Extension]),False) then begin
s:=ReadString('');
if SameText(s,Typename) then
WriteString('','');
CloseKey;
end;
DeleteKey(Format('\Software\Classes\%s\shell\open\command',[Typename]));
DeleteKey(Format('\Software\Classes\%s\shell\open',[Typename]));
DeleteKey(Format('\Software\Classes\%s\shell',[Typename]));
DeleteKey(Format('\Software\Classes\%s\DefaultIcon',[Typename]));
DeleteKey(Format('\Software\Classes\%s',[Typename]));
{RootKey:=HKEY_CLASSES_ROOT;
if OpenKey(Extension,False) then begin
s:=ReadString('');
CloseKey;
DeleteKey(Extension);
DeleteKey(s);
end;}
finally
Free;
end;
SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_IDLIST,nil,nil);
end;
function GetShellExtension(Extension,Typename: string): string;
var
i: Integer;
p: integer;
begin
p:=pos('.',Extension);
while p>0 do begin
Delete(Extension,p,1);
p:=pos('.',Extension);
end;
if (Extension='') or (Typename='') then
Exit;
Extension:='.'+Extension;
Result:='';
with TRegistry.Create do
try
RootKey:=HKEY_CURRENT_USER;
if OpenKeyReadOnly(Format('\Software\Classes\%s\shell\open\command',[Typename])) then begin
Result:=ReadString('');
CloseKey;
end;
{RootKey:=HKEY_CLASSES_ROOT;
if OpenKeyReadOnly(Extension) then begin
s:=ReadString('');
CloseKey;
if OpenKeyReadOnly(s+'\shell\open\command') then begin
Result:=ReadString('');
CloseKey;
end;
end;}
finally
Free;
end;
i:=Pos('"',Result);
if i=1 then begin
Result:=Copy(Result,i+1,Length(Result)-1);
i:=Pos('"',Result);
Result:=Copy(Result,1,i-1);
end else
Result:=Trim(Copy(Result,1,i-1));
end;
procedure AddCMAction;
begin
with TRegistry.Create do
try
RootKey:=HKEY_CLASSES_ROOT;
if ActionName='' then
ActionName:=MenuCaption;
if Copy(RegistryKey,1,1)='.' then
RegistryKey:=Copy(RegistryKey,2,MaxInt)+'_auto_file';
if Copy(RegistryKey,Length(RegistryKey),1)<>'\' then
RegistryKey:=RegistryKey+'\';
if Copy(ActionName,Length(ActionName),1)<>'\' then
ActionName:=ActionName+'\';
if OpenKey(RegistryKey+'Shell\'+ActionName,True) then begin
WriteString('',MenuCaption);
CloseKey;
end;
if OpenKey(RegistryKey+'Shell\'+ActionName+'Command\',True) then
WriteString('',ActionName);
finally
Free;
end;
end;
procedure RemoveCMAction;
begin
with TRegistry.Create do
try
RootKey:=HKEY_CLASSES_ROOT;
if Copy(RegistryKey,1,1)='.' then
RegistryKey:=Copy(RegistryKey,2,MaxInt)+'_auto_file';
if Copy(RegistryKey,Length(RegistryKey),1)<>'\' then
RegistryKey:=RegistryKey+'\';
if OpenKey('\'+RegistryKey+'shell\',True) then begin
if KeyExists(ActionName) then
DeleteKey(ActionName);
CloseKey;
end;
finally
Free;
end;
end;
procedure AddCMNew;
begin
with TRegistry.Create do
try
RootKey:=HKEY_CLASSES_ROOT;
if KeyExists(Extension) then begin
if OpenKey(Extension+'\ShellNew',True) then
case EntryType of
etNullFile: WriteString('NullFile', '');
etFileName: WriteString('FileName',Params);
etCommand: WriteString('Command',Params);
end;
CloseKey;
end;
finally
Free;
end;
end;
procedure RemoveCMNew;
begin
with TRegistry.Create do
try
RootKey:=HKEY_CLASSES_ROOT;
if KeyExists(Extension) then
if OpenKey(Extension,True) then begin
if KeyExists('ShellNew') then
DeleteKey('ShellNew');
CloseKey;
end;
finally
Free;
end;
end;
const
LinkExt = '.lnk';
IID_IPersistFile: TGUID = (
D1:$0000010B;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));
{procedure CreateLinkToFile(FileName: string; LinkPath: string; LinkCaption: string);
begin
FileName:=ExpandFileName(FileName);
if LinkPath[Length(LinkPath)] <> '\' then LinkPath:=LinkPath + '\';
LinkPath:=ExtractFilePath(ExpandFileName(LinkPath));
CreateShortCut(FileName, LinkPath + LinkCaption + LinkExt);
end;}
function ResolveLink(const LinkFile: TFileName; var ARecord: TShellLinkRecord): HRESULT;
var
wfd: TWIN32FINDDATA;
IObject: IUnknown;
ISLink: IShellLink;
IPFile: IPersistFile;
begin
ZeroMemory(@ARecord,SizeOf(ARecord));
IObject:=CreateComObject(CLSID_ShellLink);
ISLink:=IObject as IShellLink;
IPFile:=IObject as IPersistFile;
Result:=IPFile.Load(PWideChar({$IFNDEF UNICODE}ANSIToWide{$ENDIF}(LinkFile)),STGM_READ);
if Succeeded(Result) then begin
Result:=ISLink.Resolve(0,SLR_NO_UI);
if Succeeded(Result) then begin
SetLength(ARecord.Target,MAX_PATH);
SetLength(ARecord.Arguments,255);
SetLength(ARecord.WorkingDirectory,MAX_PATH);
SetLength(ARecord.IconFile,MAX_PATH);
SetLength(ARecord.Name,255);
Result:=ISLink.GetPath(PChar(ARecord.Target),MAX_PATH,wfd,SLGP_UNCPRIORITY);
if Succeeded(Result) then begin
SetLength(ARecord.Target,Length(PChar(ARecord.Target)));
// issue in 32bit code on 64bit os
if not FileExists(ARecord.Target) and (Pos(' (x86)',ARecord.Target)>0) then
ARecord.Target:=StringReplace(ARecord.Target,' (x86)','',[rfIgnorecase]);
end;
Result:=ISLink.GetArguments(PChar(ARecord.Arguments),255);
if Succeeded(Result) then
SetLength(ARecord.Arguments,Length(PChar(ARecord.Arguments)));
Result:=ISLink.GetWorkingDirectory(PChar(ARecord.WorkingDirectory),255);
if Succeeded(Result) then
SetLength(ARecord.WorkingDirectory,Length(PChar(ARecord.WorkingDirectory)));
Result:=ISLink.GetIconLocation(PChar(ARecord.IconFile),255,ARecord.IconIndex);
if Succeeded(Result) then
SetLength(ARecord.IconFile,Length(PChar(ARecord.IconFile)));
Result:=ISLink.GetDescription(PChar(ARecord.Name),255);
if Succeeded(Result) then
SetLength(ARecord.Name,Length(PChar(ARecord.Name)));
ISLink.GetShowCmd(ARecord.ShowCmd);
Result:=ISLink.GetHotkey(ARecord.HotKey);
end;
end;
end;
procedure CreateShortCut(const LinkName: string; ARecord: TShellLinkRecord);
var
IObject: IUnknown;
ISLink: IShellLink;
IPFile: IPersistFile;
begin
IObject:=CreateComObject(CLSID_ShellLink);
ISLink:=IObject as IShellLink;
IPFile:=IObject as IPersistFile;
ISLink.SetPath(PChar(ARecord.Target));
ISLink.SetArguments(PChar(ARecord.Arguments));
ISLink.SetWorkingDirectory(PChar(ARecord.WorkingDirectory));
ISLink.SetIconLocation(PChar(ARecord.IconFile),ARecord.IconIndex);
ISLink.SetDescription(PChar(ARecord.Name));
ISLink.SetShowCmd(ARecord.ShowCmd);
ISLink.SetHotkey(ARecord.HotKey);
IPFile.Save(PWideChar({$IFNDEF UNICODE}ANSIToWide{$ENDIF}(LinkName)),True);
end;
function CheckShellCM(const ASection, AName: string): boolean;
begin
Result:=False;
if (AName='') or (ASection='') then
Exit;
with TRegistry.Create do
try
RootKey:=HKEY_CLASSES_ROOT;
Result:=KeyExists(Format('%s\shell\%s',[ASection,AName]));
finally
Free;
end;
end;
procedure AddShellCM(const ASection, AName, AEXEName: string);
begin
if (AName='') or (ASection='') or (AEXEName='') then
Exit;
with TRegistry.Create do
try
RootKey:=HKEY_CLASSES_ROOT;
if OpenKey(Format('%s\shell\%s',[ASection,AName]),True) then begin
WriteString('',AName+'...');
WriteString('Icon','"'+AEXEName+'",0');
CloseKey;
end;
if OpenKey(Format('%s\shell\%s\command',[ASection,AName]),True) then begin
WriteString('','"'+AEXEName+'" "%1"');
CloseKey;
end;
finally
Free;
end;
end;
procedure RemoveShellCM(const ASection, AName: string);
begin
if (AName='') or (ASection='') then
Exit;
with TRegistry.Create do
try
RootKey:=HKEY_CLASSES_ROOT;
if KeyExists(Format('%s\shell\%s',[ASection,AName])) then
DeleteKey(Format('%s\shell\%s',[ASection,AName]));
finally
Free;
end;
end;
procedure ShowShellContextPopup(const AFilename: string; X,Y: integer; AHandle: HWND);
var
Root: IShellFolder;
ShellParentFolder: IShellFolder;
chEaten,dwAttributes: ULONG;
FilePIDL,ParentFolderPIDL: PItemIDList;
CM: IContextMenu;
Menu: HMenu;
Command: LongBool;
ICM2: IContextMenu2;
ICI: TCMInvokeCommandInfo;
ICmd: integer;
P: TPoint;
begin
OleCheck(SHGetDesktopFolder(Root));
OleCheck(Root.ParseDisplayName(AHandle,nil,PWideChar(WideString(ExtractFilePath(AFilename))),chEaten,ParentFolderPIDL,dwAttributes));
OleCheck(Root.BindToObject(ParentFolderPIDL,nil,IShellFolder,ShellParentFolder));
OleCheck(ShellParentFolder.ParseDisplayName(AHandle,nil,PWideChar(WideString(ExtractFileName(AFilename))),chEaten,FilePIDL,dwAttributes));
ShellParentFolder.GetUIObjectOf(AHandle,1,FilePIDL,IID_IContextMenu,nil,CM);
if not Assigned(CM) then
Exit;
P.X:=X;
P.Y:=Y;
ClientToScreen(AHandle,P);
Menu:=CreatePopupMenu;
try
CM.QueryContextMenu(Menu,0,1,$7FFF,CMF_EXPLORE or CMF_CANRENAME);
CM.QueryInterface(IID_IContextMenu2,ICM2);
try
Command:=TrackPopupMenu(Menu,TPM_LEFTALIGN or TPM_LEFTBUTTON or TPM_RIGHTBUTTON or TPM_RETURNCMD,p.X,p.Y,0,AHandle,nil);
finally
ICM2:=nil;
end;
if Command then begin
ICmd:=LongInt(Command)-1;
FillChar(ICI,SizeOf(ICI),#0);
with ICI do begin
cbSize:=SizeOf(ICI);
hWND:=0;
lpVerb:=MakeIntResourceA(ICmd);
nShow:=SW_SHOWNORMAL;
end;
CM.InvokeCommand(ICI);
end;
finally
DestroyMenu(Menu);
end;
end;
initialization
OleInitialize(nil);
finalization
OleUninitialize;
end.