123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- Unit ShellDialogs;
- {==================================================================
- Unit ShellDialogs / Version 1.0 / 06.1999
- ==================================================================
- Description:
- ============
- ShellDisplayContextMenu displays the shell's contextmenu for a
- file or directory or for multiple files.
- ShellExecuteContextCommand performs a contextmenu-action for a
- file or directory or for multiple files.
- Author:
- =======
- (c) Ingo Eckel 1999
- Sodener Weg 38
- 65812 Bad Soden
- Germany
- Credits:
- ========
- This unit is partly based on the work of
- Gerald Nunn (GXExplorer), https://gexperts.com
- and
- Brad Stowers (ItemProp)
- ==================================================================}
- {------------------------------------------------------------------
- You must pass fully qualified path names to all of these functions.
- If you are calling for a subdirectory (i.e. no filename), it is your
- responsibility to insure that subdirectories contain !NO! trailing
- backslash. Root-directories must be passed as 'C:\'.
- ShellDisplayContextMenu displays the right click menu for the given file or
- directory and processes the item selected, if any. Parent is the window
- handle for the owning window of any error messages that may need to be
- displayed by the system, MyForm.Handle is generally fine. Pos is the X, Y
- position to display the menu at given in screen (absolute) coordinates.
- ------------------------------------------------------------------}
- Interface
- Uses Windows, ShlObj, SysUtils, Classes, Messages, Menus,
- PIDL;
- {Commands for ShellExecuteContextCommand:}
- Const shcProperties = 'properties';
- shcCut = 'cut';
- shcCopy = 'copy';
- shcPaste = 'paste';
- shcDelete = 'delete';
- shcLink = 'link';
- shcrename = 'rename';
- shcDefault = '';
- Type PPIDLArray = ^TPIDLArray;
- TPIDLArray = Array [0..0] of PItemIDList;
- {Display the shell's contextmenu to a file or directory.
- Requires the iShellFolder-interface to the parent directory and the
- PIDLs of the files or directories:}
- Procedure ShellDisplayContextMenu(Handle: THandle;
- P: TPoint;
- ShellFolder: IShellFolder;
- PIDLCount: Integer;
- Var PIDL: PItemIDList;
- AllowRename : Boolean;
- Var Verb: String;
- PerformPaste : Boolean = True); Overload;
- {Display the shell's contextmenu to single file or directory.
- Requires the full qualified name of the file or directory:}
- Procedure ShellDisplayContextMenu(Handle: THandle;
- P: TPoint;
- FileName : String;
- AllowRename : Boolean;
- Var Verb: String;
- PerformPaste : Boolean = True); Overload;
- {Display the shell's contextmenu to muliple files or directories.
- Requires the full qualified name of the parent directory and the
- filenames of the files or directories as TStringList:}
- Procedure ShellDisplayContextMenu(Handle: THandle;
- P: TPoint;
- Path : String;
- Files : TStringList;
- Var Verb: String;
- PerformPaste : Boolean = True); Overload;
- {Performs a contextmenu-command (properties, copy, cut, paste) for files or directories.
- Requires the iShellFolder-interface to the parent directory and the
- PIDLs of the files or directories:}
- Function ShellExecuteContextCommand(Handle: THandle;
- Command: String;
- ShellFolder: IShellFolder;
- PIDLCount: Integer;
- Var PIDL: PItemIDList): Boolean; Overload;
- {Performs a contextmenu-command (properties, copy, cut, paste) for a file or directory.
- Requires the full qualified name of the file or directory:}
- Function ShellExecuteContextCommand(Handle: THandle;
- Command: String;
- FileName : String): Boolean; Overload;
- {Performs a contextmenu-command (properties, copy, cut, paste) for
- multiple files or directories.
- Requires the full qualified name of the parent directory and the
- filenames of the files or directories as TStringList:}
- Function ShellExecuteContextCommand(Handle: THandle;
- Command : String;
- Path : String;
- Files : TStringList) : Boolean; Overload;
- Var CustomContextMenu : TPopupMenu;
- {------------------------------------------------------------------}
- Implementation
- {------------------------------------------------------------------}
- {$R-}
- function MenuCallbackProc(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LResult; stdcall; export;
- var CM2: IContextMenu2;
- begin
- case Msg of
- WM_CREATE:
- begin
- CM2 := IContextMenu2(PCreateStruct(lParam).lpCreateParams);
- SetWindowLong(Wnd, GWL_USERDATA, LongInt(CM2));
- Result := DefWindowProc(Wnd, Msg, wParam, lParam);
- end;
- // these are the biggies -- the messages that IContextMenu2::HandlMenuMsg is
- // supposed to handle.
- WM_DRAWITEM,
- WM_MEASUREITEM,
- WM_INITMENUPOPUP:
- begin
- begin
- CM2 := IContextMenu2(GetWindowLong(Wnd, GWL_USERDATA));
- Assert(CM2 <> NIL, 'NIL Context Menu!');
- CM2.HandleMenuMsg(Msg, wParam, lParam);
- end;
- if Msg = WM_INITMENUPOPUP then
- Result := 0
- else
- Result := 1;
- end;
- else
- Result := DefWindowProc(Wnd, Msg, wParam, lParam);
- end;
- end; {MenuCallBackProc}
- Procedure ShellDisplayContextMenu(Handle: THandle;
- P: TPoint;
- ShellFolder: IShellFolder;
- PIDLCount: Integer;
- Var PIDL: PItemIDList;
- AllowRename : Boolean;
- Var Verb: String;
- PerformPaste : Boolean = True);
- Const CallBackClassName = 'ShellDialogsCallBack';
- CallBackProcessor = 'ShellDialogsCallBackProcessor';
- MaxStdEntries = 1000;
- Var PopupMenu : HMenu;
- Cmd : Cardinal;
- ICM : TCMInvokeCommandInfo;
- ContextMenu : IContextMenu;
- ContextMenu2 : IContextMenu2;
- Flags : UINT;
- AWndClass : TWndClass;
- CallbackWnd : HWnd;
- MenuHandle : HWnd;
- i : Integer;
- Begin
- Verb := EmptyStr;
- CallBackWnd := 0;
- If AllowRename then
- Flags := CMF_EXPLORE Or CMF_CANRENAME
- Else
- Flags := CMF_EXPLORE;
- PopupMenu := CreatePopupMenu;
- Try
- If Succeeded(ShellFolder.GetUIObjectOf(Handle, PIDLCount, PIDL, IID_IContextMenu, NIL, Pointer(ContextMenu))) Then
- Begin
- ContextMenu._AddRef;
- If Succeeded(ContextMenu.QueryInterface(IID_IContextMenu2, ContextMenu2)) Then
- Begin
- ContextMenu2._AddRef;
- Try
- ContextMenu2.QueryContextMenu(PopupMenu, 0, 1, MaxStdEntries, Flags);
- Except
- Exit;
- End;
- FillChar(AWndClass, SizeOf(AWndClass), #0);
- AWndClass.lpszClassName := CallBackClassName;
- AWndClass.Style := CS_PARENTDC;
- AWndClass.lpfnWndProc := @MenuCallbackProc;
- AWndClass.hInstance := HInstance;
- Windows.RegisterClass(AWndClass);
- CallbackWnd := CreateWindow(CallBackClassName, CallBackProcessor, WS_POPUPWINDOW, 0, 0, 0, 0, 0, 0, HInstance, Pointer(ContextMenu2));
- End
- Else
- Try
- ContextMenu.QueryContextMenu(PopupMenu, 0, 1, $7FFF, Flags);
- Except
- Exit;
- End;
- If CallbackWnd = 0 then
- MenuHandle := Handle
- else
- MenuHandle := CallbackWnd;
- IF Assigned(CustomContextMenu) And (CustomContextMenu.Items.Count > 0) Then
- Begin
- AppendMenu(PopupMenu, MF_SEPARATOR, 0, NIL);
- For i := 0 To CustomContextMenu.Items.Count -1 Do
- With CustomContextMenu.Items[i] Do
- IF Visible And Assigned(OnClick) Then
- Begin
- Flags := MF_STRING;
- IF Checked Then
- Flags := Flags Or MF_CHECKED;
- AppendMenu(PopUpMenu, Flags, MaxStdEntries + i + 1, PChar(Caption));
- End;
- End;
- Cmd := Cardinal(TrackPopupMenuEx(PopupMenu,TPM_LEFTALIGN Or TPM_RIGHTBUTTON Or TPM_RETURNCMD, P.X, P.Y, MenuHandle, NIL));
- IF Cmd > MaxStdEntries Then
- Begin
- IF Assigned(CustomContextMenu.Items[Cmd - MaxStdEntries - 1].OnClick) Then
- CustomContextMenu.Items[Cmd - MaxStdEntries - 1].OnClick(NIL);
- End
- Else
- if Cmd > 0 then
- Begin
- SetLength(Verb, 255);
- If Assigned(ContextMenu2) then
- ContextMenu2.GetCommandString(Cardinal(MakeIntResource(Cmd-1)), GCS_VERB, NIL,PAnsiChar(PChar(Verb)), Length(Verb))
- Else
- ContextMenu.GetCommandString(Cardinal(MakeIntResource(Cmd-1)),GCS_VERB, NIL, PAnsiChar(PChar(Verb)), Length(Verb));
- SetLength(Verb, strlen(PChar(Verb)));
- Verb := LowerCase(Verb);
- If (Verb <> shcRename) And (PerformPaste Or (Verb <> shcPaste)) Then
- Begin
- FillChar(ICM,SizeOf(TCMInvokeCommandInfo),#0);
- ICM.cbSize := Sizeof(TCMInvokeCommandInfo);
- ICM.hwnd := Handle;
- ICM.lpVerb := LPCSTR(MakeIntResource(Cmd-1));
- ICM.nShow := SW_SHOWNORMAL;
- Try
- If Assigned(ContextMenu2) Then
- ContextMenu2.InvokeCommand(ICM)
- Else
- ContextMenu.InvokeCommand(ICM);
- Except
- // eat any dammned shell exceptions!
- Exit;
- End;
- End;
- End;
- End;
- Finally
- DestroyMenu(PopupMenu);
- If CallbackWnd <> 0 Then
- DestroyWindow(CallbackWnd);
- IF Assigned(ContextMenu2) Then
- ContextMenu2._Release;
- IF Assigned(ContextMenu) Then
- ContextMenu._Release;
- End;
- End; {ShellDisplayContextMenu (PIDL) }
- Procedure ShellDisplayContextMenu(Handle: THandle;
- P: TPoint;
- FileName : String;
- AllowRename : Boolean;
- Var Verb: String;
- PerformPaste : Boolean = True);
- Var ShellFolder : iShellFolder;
- DirPIDL : PItemIDList;
- DirPIDLFQ : PItemIDList;
- ParentPIDL : PItemIDList;
- Begin
- DirPIDL := NIL;
- DirPIDLFQ := NIL;
- ParentPIDL := NIL;
- DirPidlFQ := PIDL_GetFromPath(PChar(FileName));
- IF Assigned(DirPIDLFQ) Then
- Begin
- PIDL_GetRelative(DirPIDLFQ, ParentPIDL, DirPIDL);
- Try
- If PIDL_GetFileFolder(ParentPIDL, ShellFolder) Then
- ShellDisplayContextMenu(Handle, P, ShellFolder, 1, DirPIDL, AllowRename, Verb, PerformPaste);
- Finally
- PIDL_Free(DirPIDL);
- PIDL_Free(DirPIDLFQ);
- PIDL_Free(ParentPIDL);
- End;
- End;
- End; {ShellDisplayContextMenu (Filename) }
- Procedure ShellDisplayContextMenu(Handle: THandle;
- P: TPoint;
- Path : String;
- Files : TStringList;
- Var Verb: String;
- PerformPaste : Boolean = True);
- Var ShellFolder : iShellFolder;
- PathPIDL : PItemIDList;
- PIDLArray : PPIDLArray;
- Index : Integer;
- i : Integer;
- Begin
- IF Files.Count = 0 Then
- Exit;
- Index := 0;
- GetMem(PIDLArray, SizeOf(PItemIDList) * Files.Count);
- FillChar(PIDLArray^, Sizeof(PItemIDList) * Files.Count, #0);
- Try
- PathPIDL := PIDL_GetFromPath(PChar(Path));
- IF Assigned(PathPIDL) Then
- Begin
- Try
- If PIDL_GetFileFolder(PathPIDL, ShellFolder) Then
- Begin
- For i := 0 To Files.Count - 1 Do
- Begin
- PIDLArray^[i] := PIDL_GetFromParentFolder(ShellFolder, PChar(Files[i]));
- IF Assigned(PIDLArray^[i]) Then
- INC(Index);
- End;
- IF Index > 0 Then
- Begin
- Try
- ShellDisplayContextMenu(Handle, P, ShellFolder, Index, PIDLArray^[0], False, Verb, PerformPaste);
- Finally
- For i := 0 To Index - 1 Do
- PIDL_Free(PIDLArray[i]);
- End;
- End;
- End;
- Finally
- PIDL_Free(PathPIDL);
- End;
- End;
- Finally
- FreeMem(PIDLArray);
- End;
- End; {ShellDisplayContextMenu (TStringList) }
- Function ShellExecuteContextCommand(Handle: THandle; Command: String; ShellFolder: IShellFolder; PIDLCount: Integer; Var PIDL: PItemIDList): Boolean;
- Var ICM : TCMInvokeCommandInfoEx;
- ContextMenu : IContextMenu;
- ContextMenu2: IContextMenu2;
- Popup : HMenu;
- MenuCmd : Cardinal;
- HRes : HResult;
- Begin
- Result := False;
- IF Succeeded(ShellFolder.GetUIObjectOf(Handle, PIDLCount, PIDL, IID_IContextMenu, NIL, Pointer(ContextMenu))) Then
- Begin
- ContextMenu.QueryInterface(IID_IContextMenu2, ContextMenu2);
- FillChar(ICM,SizeOf(TCMInvokeCommandInfo), #0);
- ICM.hwnd := Handle;
- ICM.cbSize := SizeOf(TCMInvokeCommandInfo);
- ICM.nShow := SW_SHOWNORMAL;
- IF Command <> shcDefault Then
- begin
- ICM.fMask := CMIC_MASK_UNICODE;
- ICM.lpVerb := PAnsiChar(AnsiString(Command));
- ICM.lpVerbW := PChar(Command);
- end
- Else
- Begin
- {Locate the menuitem for the default action:}
- Popup := CreatePopupMenu;
- Try
- Try
- IF Assigned(ContextMenu2) Then
- Hres := ContextMenu2.QueryContextMenu(Popup, 0, 1, $7FFF, CMF_DEFAULTONLY)
- Else
- Hres := ContextMenu.QueryContextMenu(Popup, 0, 1, $7FFF, CMF_DEFAULTONLY);
- Except
- Exit;
- End;
- If Succeeded(HRes) Then
- Begin
- MenuCmd := GetMenuDefaultItem(Popup, 0, 0);
- If MenuCmd <> $FFFFFFFF then
- ICM.lpVerb := LPCSTR(MakeIntResource(MenuCmd-1))
- Else
- ICM.lpVerb := NIL;
- end;
- finally
- DestroyMenu(Popup);
- end;
- End;
- Try
- If Assigned(ContextMenu2) then
- Result := Succeeded(ContextMenu2.InvokeCommand(PCMInvokeCommandInfo(@ICM)^))
- Else
- Result := Succeeded(ContextMenu.InvokeCommand(PCMInvokeCommandInfo(@ICM)^));
- Except
- // eat any dammned shell exceptions.
- End;
- End;
- End; {ShellExecuteContextCommand (PIDL)}
- Function ShellExecuteContextCommand(Handle: THandle; Command: String; FileName : String): Boolean;
- Var ShellFolder : iShellFolder;
- DirPIDL : PItemIDList;
- DirPIDLFQ : PItemIDList;
- ParentPIDL : PItemIDList;
- Begin
- DirPIDL := NIL;
- DirPIDLFQ := NIL;
- ParentPIDL := NIL;
- Result := False;
- DirPidlFQ := PIDL_GetFromPath(PChar(FileName));
- IF Assigned(DirPIDLFQ) Then
- Begin
- PIDL_GetRelative(DirPIDLFQ, ParentPIDL, DirPIDL);
- Try
- If PIDL_GetFileFolder(ParentPIDL, ShellFolder) Then
- Result := ShellExecuteContextCommand(Handle, Command, ShellFolder, 1, DirPIDL);
- Finally
- PIDL_Free(DirPIDL);
- PIDL_Free(DirPIDLFQ);
- PIDL_Free(ParentPIDL);
- End;
- End;
- End; {ShellExecuteContextCommand (FileName)}
- Function ShellExecuteContextCommand(Handle: THandle;
- Command : String;
- Path : String;
- Files : TStringList) : Boolean;
- Var ShellFolder : iShellFolder;
- PathPIDL : PItemIDList;
- PIDLArray : PPIDLArray;
- Index : Integer;
- i : Integer;
- Begin
- Result := False;
- IF Files.Count = 0 Then
- Exit;
- Index := 0;
- GetMem(PIDLArray, SizeOf(PItemIDList) * Files.Count);
- FillChar(PIDLArray^, Sizeof(PItemIDList) * Files.Count, #0);
- Try
- PathPIDL := PIDL_GetFromPath(PChar(Path));
- IF Assigned(PathPIDL) Then
- Begin
- Try
- If PIDL_GetFileFolder(PathPIDL, ShellFolder) Then
- Begin
- For i := 0 To Files.Count - 1 Do
- Begin
- PIDLArray^[i] := PIDL_GetFromParentFolder(ShellFolder, PChar(Files[i]));
- IF Assigned(PIDLArray^[i]) Then
- INC(Index);
- End;
- IF Index > 0 Then
- Begin
- Try
- Result := ShellExecuteContextCommand(Handle, Command, ShellFolder, Index, PIDLArray^[0]);
- Finally
- For i := 0 To Index - 1 Do
- PIDL_Free(PIDLArray^[i]);
- End;
- End;
- End;
- Finally
- PIDL_Free(PathPIDL);
- End;
- End;
- Finally
- FreeMem(PIDLArray);
- End;
- End; {ShellExecuteContextCommand (TStringList) }
- End.
|