Link to home
Start Free TrialLog in
Avatar of CalvinDay
CalvinDay

asked on

Windows sub-menu

Referencing Q.10189074. You can use the registry to create a Windows Explorer Menu popup. How do you add a sub menu popup?
Avatar of inthe
inthe

also curious..
A shell extension is needed here. I don't know a way to do that without. It's quite complicated.
I also think that you can't create sub menus without a shell extension except the "sent to" menu there it's already done by windows.
Yep, that's a shell extension too. I checked that out before I posted my previous comment.

CalvinDay, I may be able to solve this problem but it will take far too much time that I don't have right now.

Delphi 3 and 4 come with a context menu demo (contmenu.dpr) which add an item to the menu when you right-click drag-and-drop. That demo can be modified to construct a submenu.
Then the DLL must be registered the right way.

It's tricky stuff so be carefull.

Eps.
Avatar of CalvinDay

ASKER

I couldn't get contmenu.dpr to work. I then realized I must copy contmenu.dll to C:\windows\system. It works fine.
So far I've added this. (and it works)

function TContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
          idCmdLast, uFlags: UINT): HResult;
var
  popMenu:HMENU;
begin
  popMenu:=CreatePopupMenu;
  InsertMenu(popMenu,0,MF_STRING or MF_BYPOSITION,0,'Sub 1');
  InsertMenu(popMenu,1,MF_STRING or MF_BYPOSITION,1,'Sub 2');
  InsertMenu(popMenu,2,MF_STRING or MF_BYPOSITION,2,'Sub 3');
  InsertMenu(popMenu,3,MF_STRING or MF_BYPOSITION,3,'Sub 4');
  InsertMenu(Menu,indexMenu,MF_STRING or MF_POPUP,popMenu,'Compile...');
  Result := 1;
end;

Other posting was wrong, this is correct: I have written a little utility to sort file names and print them according to sort criteria. It is called xprint.exe. Now with this little shell extension, I can highlight a group of files, right click and select the printed sort order on the fly.

Here's the final code:

unit ContextM;

interface
uses
    Windows, ComObj, ComServ, ShlObj, ActiveX, ShellApi, SysUtils, Classes;

Const
   CLSID_ContextMenuShellExtension: TGUID = (
    D1:$8e3e0f0a; D2:$0fcc; D3:$11ce; D4:($bc, $b0, $b3, $fd, $0e, $25, $38, $1a));

type
    TContextMenu = class(TComObject, IShellExtInit, IContextMenu)
    private
      szFile: array[0..MAX_PATH] of Char;
    public
      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;
      function Initialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
        hKeyProgID: HKEY): HResult; stdcall;
    end;

var
  t:TStringStream;

implementation

function GetCompilerPath(sort:integer): string;
const
  SortName='ntsd';
var
  sWinDir,s:string;
  l:integer;
begin
  SetLength(sWinDir,255);
  l:=GetWindowsDirectory(PChar(sWinDir),255);
  SetLength(sWinDir,l);
  Result := sWinDir+'\xPrint.exe /'+SortName[sort+1]+' %s';
end;

function TContextMenu.QueryContextMenu(Menu:HMENU;indexMenu,idCmdFirst,idCmdLast,uFlags:UINT):HResult;
const
  CmdName: array[0..3] of string=('by &Name','by &Type','by &Size','by &Date');
var
  popMenu:HMENU;
  i:integer;
begin
  popMenu:=CreatePopupMenu;
  for i:=0 to High(CmdName) do
    InsertMenu(popMenu,i,MF_STRING or MF_BYCOMMAND,idCmdFirst+i,PChar(CmdName[i]));
  InsertMenu(Menu,indexMenu,MF_STRING or MF_POPUP,popMenu,'xPrint ...');
  Result:=High(CmdName)+1;
end;

function TContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
var
  H: THandle;
begin
  Result:=E_FAIL;
  if HiWord(Integer(lpici.lpVerb))<>0 then exit;
  H:=WinExec(PChar(Format(GetCompilerPath(LoWord(lpici.lpVerb)),[t.DataString])),lpici.nShow);
  if H<32 then
    MessageBox(lpici.hWnd, 'Error executing xPrint.', 'Error',MB_ICONERROR or MB_OK);
end;

function TContextMenu.GetCommandString(idCmd,uType:UINT;pwReserved:PUINT;pszName:LPSTR;cchMax:UINT):HRESULT;
const
  CmdHint: array[0..3] of string=('Name','Type','Size','Date');
begin
  Result:=NOERROR;
  if (idCmd>=0) and (idCmd<=High(CmdHint)) then
    StrPCopy(pszName,'Prints selected items sorted by '+CmdHint[idCmd])
  else
    Result:=E_INVALIDARG;
end;

function TContextMenu.Initialize(pidlFolder:PItemIDList;lpdobj:IDataObject;hKeyProgID:HKEY):HResult;
var
  medium: TStgMedium;
  fe: TFormatEtc;
  i,count:integer;
begin
  with fe do
    begin
    cfFormat:=CF_HDROP;
    ptd:=nil;
    dwAspect:=DVASPECT_CONTENT;
    lindex:=-1;
    tymed:=TYMED_HGLOBAL;
    end;

  Result:=E_FAIL;
  if lpdobj=nil then exit;

  Result:=lpdobj.GetData(fe,medium);
  if Failed(Result) then exit;

  count:=DragQueryFile(medium.hGlobal,$FFFFFFFF,nil,0);
  t.Position:=0;
  for i:=0 to count-1 do
    begin
    DragQueryFile(medium.hGlobal,i,szFile,SizeOf(szFile));
    t.WriteString('"'+string(szFile)+'" ');
    end;

  Result:=NOERROR;
  ReleaseStgMedium(medium);
end;

initialization
  t:=TStringStream.Create('');
  TComObjectFactory.Create(ComServer, TContextMenu, CLSID_ContextMenuShellExtension,
         '', 'Delphi 3.0 ContextMenu Example', ciMultiInstance);
finalization
  t.Free;
end.

Epsylon, please post an answer for your wonderful suggestion.


ASKER CERTIFIED SOLUTION
Avatar of Epsylon
Epsylon

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks, epsylon, I did learn alot. The only thing I'm missing is the little bitmap next to the menu. I may have to post a question to do that.
 
If anyone wants the xPrint program, drop me your email. it's pretty good when trying to compare dll's and such.