2012 ...
januari (3) februari (5) maart april (6) mei (2) juni (1) juli (2) augustus september (5) oktober (1) november (2) december
2012-11-07 20:42
r1758
[permalink]
v1.0.5.320
How to create a shell extension context menu.
2012-11-12 16:41
i3045
[permalink]
I've put it up here, but since it's a pretty good boiler plate code, I'll put it up here as well. Include a unit like this one into an ActiveX library. I especially had a though time to get the values right that are based on idCmdFirst and get sent back when a menu item is invoked. Something that gives pretty strange results if you don't get it right.
unit demoContextMenu;
interface
uses
Windows, Classes, ActiveX, ComObj, ShlObj;
type
PItemIDList=LPCITEMIDLIST;
{ TContextMenu }
TContextMenu = class(TComObject, IShellExtInit, IContextMenu)
private
Files:TStringList;
protected
{ IShellExtInit }
function IShellExtInit.Initialize = SEIInitialize;
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: UINT_Ptr; uType: UINT; pwReserved: PUINT;
pszName: LPSTR; cchMax: UINT): HResult; stdcall;
public
procedure Initialize; override;
destructor Destroy; override;
end;
const
Class_ContextMenu: TGUID = '{put a new GUID here by pressing Ctrl+Shift+G}';
implementation
uses ComServ, SysUtils, Registry;
procedure TContextMenu.Initialize;
begin
inherited;
Files:=TStringList.Create;
end;
destructor TContextMenu.Destroy;
begin
Files.Free;
inherited;
end;
function TContextMenu.SEIInitialize(pidlFolder: PItemIDList;
lpdobj: IDataObject; hKeyProgID: HKEY): HResult; stdcall;
var
StgMedium: TStgMedium;
FormatEtc: TFormatEtc;
i,c:integer;
s:string;
begin
if lpdobj=nil then Result:=E_INVALIDARG else
begin
FormatEtc.cfFormat:=CF_HDROP;
FormatEtc.ptd:=nil;
FormatEtc.dwAspect:=DVASPECT_CONTENT;
FormatEtc.lindex:=-1;
FormatEtc.tymed:=TYMED_HGLOBAL;
Result:=lpdobj.GetData(FormatEtc,StgMedium);
if not(Failed(Result)) then
begin
c:=DragQueryFile(StgMedium.hGlobal,$FFFFFFFF,nil,0);
for i:=0 to c-1 do
begin
SetLength(s,1024);
SetLength(s,DragQueryFile(StgMedium.hGlobal,i,PChar(s),1024));
Files.Add(s);
end;
ReleaseStgMedium(StgMedium);
Result:=NOERROR;
end;
end;
end;
function TContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
idCmdLast, uFlags: UINT): HResult; stdcall;
var
h:HMENU;
i:integer;
begin
i:=1;
h:=CreatePopupMenu;
AppendMenu(h,MF_STRING,idCmdFirst+i,'Menu item one'); inc(i);
AppendMenu(h,MF_STRING,idCmdFirst+i,'Menu item two'); inc(i);
AppendMenu(h,MF_STRING,idCmdFirst+i,'Menu item three'); inc(i);
InsertMenu(Menu,indexMenu,
MF_BYPOSITION or MF_POPUP or MF_STRING,h,'DemoContextMenu');
Result:=i;
end;
function TContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
stdcall;
begin
Result := E_FAIL;
//not called by application
if HiWord(Integer(lpici.lpVerb))=0 then
begin
Result := NOERROR;
case LoWord(Integer(lpici.lpVerb)) of
1:;//perform action one (use data in Files:TStringList)
2:;//perform action two
3:;//perform action three
else Result := E_INVALIDARG;
end;
end;
end;
function TContextMenu.GetCommandString(idCmd: UINT_Ptr; uType: UINT;
pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult; stdcall;
begin
if idCmd=0 then
begin
if (uType=GCS_HELPTEXTW) then
StrCopy(pszName,'Perform one of several functions on files');
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;
r:TRegistry;
begin
if Register then
begin
inherited UpdateRegistry(Register);
ClassID := GUIDToString(Class_ContextMenu);
CreateRegKey('*\shellex', '', '');
CreateRegKey('*\shellex\ContextMenuHandlers', '', '');
CreateRegKey('*\shellex\ContextMenuHandlers\DemoContextMenu', '', ClassID);
CreateRegKey('Folder\shellex', '', '');
CreateRegKey('Folder\shellex\ContextMenuHandlers', '', '');
CreateRegKey('Folder\shellex\ContextMenuHandlers\DemoContextMenu', '', ClassID);
if Win32Platform=VER_PLATFORM_WIN32_NT then
begin
r:=TRegistry.Create;
try
r.RootKey:=HKEY_LOCAL_MACHINE;
r.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions',True);
r.OpenKey('Approved',True);
r.WriteString(ClassID,'DemoContextMenu Shell Extension');
finally
r.Free;
end;
end;
end
else
begin
DeleteRegKey('Folder\shellex\ContextMenuHandlers\DemoContextMenu');
DeleteRegKey('*\shellex\ContextMenuHandlers\DemoContextMenu');
inherited UpdateRegistry(Register);
end;
end;
initialization
TContextMenuFactory.Create(ComServer, TContextMenu, Class_ContextMenu,
'', 'DemoContextMenu Shell Extension', ciMultiInstance, tmApartment);
end.