ShellDialogs.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. Unit ShellDialogs;
  2. {==================================================================
  3. Unit ShellDialogs / Version 1.0 / 06.1999
  4. ==================================================================
  5. Description:
  6. ============
  7. ShellDisplayContextMenu displays the shell's contextmenu for a
  8. file or directory or for multiple files.
  9. ShellExecuteContextCommand performs a contextmenu-action for a
  10. file or directory or for multiple files.
  11. Author:
  12. =======
  13. (c) Ingo Eckel 1999
  14. Sodener Weg 38
  15. 65812 Bad Soden
  16. Germany
  17. Credits:
  18. ========
  19. This unit is partly based on the work of
  20. Gerald Nunn (GXExplorer), https://gexperts.com
  21. and
  22. Brad Stowers (ItemProp)
  23. ==================================================================}
  24. {------------------------------------------------------------------
  25. You must pass fully qualified path names to all of these functions.
  26. If you are calling for a subdirectory (i.e. no filename), it is your
  27. responsibility to insure that subdirectories contain !NO! trailing
  28. backslash. Root-directories must be passed as 'C:\'.
  29. ShellDisplayContextMenu displays the right click menu for the given file or
  30. directory and processes the item selected, if any. Parent is the window
  31. handle for the owning window of any error messages that may need to be
  32. displayed by the system, MyForm.Handle is generally fine. Pos is the X, Y
  33. position to display the menu at given in screen (absolute) coordinates.
  34. ------------------------------------------------------------------}
  35. Interface
  36. Uses Windows, ShlObj, SysUtils, Classes, Messages, Menus,
  37. PIDL;
  38. {Commands for ShellExecuteContextCommand:}
  39. Const shcProperties = 'properties';
  40. shcCut = 'cut';
  41. shcCopy = 'copy';
  42. shcPaste = 'paste';
  43. shcDelete = 'delete';
  44. shcLink = 'link';
  45. shcrename = 'rename';
  46. shcDefault = '';
  47. Type PPIDLArray = ^TPIDLArray;
  48. TPIDLArray = Array [0..0] of PItemIDList;
  49. {Display the shell's contextmenu to a file or directory.
  50. Requires the iShellFolder-interface to the parent directory and the
  51. PIDLs of the files or directories:}
  52. Procedure ShellDisplayContextMenu(Handle: THandle;
  53. P: TPoint;
  54. ShellFolder: IShellFolder;
  55. PIDLCount: Integer;
  56. Var PIDL: PItemIDList;
  57. AllowRename : Boolean;
  58. Var Verb: String;
  59. PerformPaste : Boolean = True); Overload;
  60. {Display the shell's contextmenu to single file or directory.
  61. Requires the full qualified name of the file or directory:}
  62. Procedure ShellDisplayContextMenu(Handle: THandle;
  63. P: TPoint;
  64. FileName : String;
  65. AllowRename : Boolean;
  66. Var Verb: String;
  67. PerformPaste : Boolean = True); Overload;
  68. {Display the shell's contextmenu to muliple files or directories.
  69. Requires the full qualified name of the parent directory and the
  70. filenames of the files or directories as TStringList:}
  71. Procedure ShellDisplayContextMenu(Handle: THandle;
  72. P: TPoint;
  73. Path : String;
  74. Files : TStringList;
  75. Var Verb: String;
  76. PerformPaste : Boolean = True); Overload;
  77. {Performs a contextmenu-command (properties, copy, cut, paste) for files or directories.
  78. Requires the iShellFolder-interface to the parent directory and the
  79. PIDLs of the files or directories:}
  80. Function ShellExecuteContextCommand(Handle: THandle;
  81. Command: String;
  82. ShellFolder: IShellFolder;
  83. PIDLCount: Integer;
  84. Var PIDL: PItemIDList): Boolean; Overload;
  85. {Performs a contextmenu-command (properties, copy, cut, paste) for a file or directory.
  86. Requires the full qualified name of the file or directory:}
  87. Function ShellExecuteContextCommand(Handle: THandle;
  88. Command: String;
  89. FileName : String): Boolean; Overload;
  90. {Performs a contextmenu-command (properties, copy, cut, paste) for
  91. multiple files or directories.
  92. Requires the full qualified name of the parent directory and the
  93. filenames of the files or directories as TStringList:}
  94. Function ShellExecuteContextCommand(Handle: THandle;
  95. Command : String;
  96. Path : String;
  97. Files : TStringList) : Boolean; Overload;
  98. Var CustomContextMenu : TPopupMenu;
  99. {------------------------------------------------------------------}
  100. Implementation
  101. {------------------------------------------------------------------}
  102. {$R-}
  103. function MenuCallbackProc(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LResult; stdcall; export;
  104. var CM2: IContextMenu2;
  105. begin
  106. case Msg of
  107. WM_CREATE:
  108. begin
  109. CM2 := IContextMenu2(PCreateStruct(lParam).lpCreateParams);
  110. SetWindowLong(Wnd, GWL_USERDATA, LongInt(CM2));
  111. Result := DefWindowProc(Wnd, Msg, wParam, lParam);
  112. end;
  113. // these are the biggies -- the messages that IContextMenu2::HandlMenuMsg is
  114. // supposed to handle.
  115. WM_DRAWITEM,
  116. WM_MEASUREITEM,
  117. WM_INITMENUPOPUP:
  118. begin
  119. begin
  120. CM2 := IContextMenu2(GetWindowLong(Wnd, GWL_USERDATA));
  121. Assert(CM2 <> NIL, 'NIL Context Menu!');
  122. CM2.HandleMenuMsg(Msg, wParam, lParam);
  123. end;
  124. if Msg = WM_INITMENUPOPUP then
  125. Result := 0
  126. else
  127. Result := 1;
  128. end;
  129. else
  130. Result := DefWindowProc(Wnd, Msg, wParam, lParam);
  131. end;
  132. end; {MenuCallBackProc}
  133. Procedure ShellDisplayContextMenu(Handle: THandle;
  134. P: TPoint;
  135. ShellFolder: IShellFolder;
  136. PIDLCount: Integer;
  137. Var PIDL: PItemIDList;
  138. AllowRename : Boolean;
  139. Var Verb: String;
  140. PerformPaste : Boolean = True);
  141. Const CallBackClassName = 'ShellDialogsCallBack';
  142. CallBackProcessor = 'ShellDialogsCallBackProcessor';
  143. MaxStdEntries = 1000;
  144. Var PopupMenu : HMenu;
  145. Cmd : Cardinal;
  146. ICM : TCMInvokeCommandInfo;
  147. ContextMenu : IContextMenu;
  148. ContextMenu2 : IContextMenu2;
  149. Flags : UINT;
  150. AWndClass : TWndClass;
  151. CallbackWnd : HWnd;
  152. MenuHandle : HWnd;
  153. i : Integer;
  154. Begin
  155. Verb := EmptyStr;
  156. CallBackWnd := 0;
  157. If AllowRename then
  158. Flags := CMF_EXPLORE Or CMF_CANRENAME
  159. Else
  160. Flags := CMF_EXPLORE;
  161. PopupMenu := CreatePopupMenu;
  162. Try
  163. If Succeeded(ShellFolder.GetUIObjectOf(Handle, PIDLCount, PIDL, IID_IContextMenu, NIL, Pointer(ContextMenu))) Then
  164. Begin
  165. ContextMenu._AddRef;
  166. If Succeeded(ContextMenu.QueryInterface(IID_IContextMenu2, ContextMenu2)) Then
  167. Begin
  168. ContextMenu2._AddRef;
  169. Try
  170. ContextMenu2.QueryContextMenu(PopupMenu, 0, 1, MaxStdEntries, Flags);
  171. Except
  172. Exit;
  173. End;
  174. FillChar(AWndClass, SizeOf(AWndClass), #0);
  175. AWndClass.lpszClassName := CallBackClassName;
  176. AWndClass.Style := CS_PARENTDC;
  177. AWndClass.lpfnWndProc := @MenuCallbackProc;
  178. AWndClass.hInstance := HInstance;
  179. Windows.RegisterClass(AWndClass);
  180. CallbackWnd := CreateWindow(CallBackClassName, CallBackProcessor, WS_POPUPWINDOW, 0, 0, 0, 0, 0, 0, HInstance, Pointer(ContextMenu2));
  181. End
  182. Else
  183. Try
  184. ContextMenu.QueryContextMenu(PopupMenu, 0, 1, $7FFF, Flags);
  185. Except
  186. Exit;
  187. End;
  188. If CallbackWnd = 0 then
  189. MenuHandle := Handle
  190. else
  191. MenuHandle := CallbackWnd;
  192. IF Assigned(CustomContextMenu) And (CustomContextMenu.Items.Count > 0) Then
  193. Begin
  194. AppendMenu(PopupMenu, MF_SEPARATOR, 0, NIL);
  195. For i := 0 To CustomContextMenu.Items.Count -1 Do
  196. With CustomContextMenu.Items[i] Do
  197. IF Visible And Assigned(OnClick) Then
  198. Begin
  199. Flags := MF_STRING;
  200. IF Checked Then
  201. Flags := Flags Or MF_CHECKED;
  202. AppendMenu(PopUpMenu, Flags, MaxStdEntries + i + 1, PChar(Caption));
  203. End;
  204. End;
  205. Cmd := Cardinal(TrackPopupMenuEx(PopupMenu,TPM_LEFTALIGN Or TPM_RIGHTBUTTON Or TPM_RETURNCMD, P.X, P.Y, MenuHandle, NIL));
  206. IF Cmd > MaxStdEntries Then
  207. Begin
  208. IF Assigned(CustomContextMenu.Items[Cmd - MaxStdEntries - 1].OnClick) Then
  209. CustomContextMenu.Items[Cmd - MaxStdEntries - 1].OnClick(NIL);
  210. End
  211. Else
  212. if Cmd > 0 then
  213. Begin
  214. SetLength(Verb, 255);
  215. If Assigned(ContextMenu2) then
  216. ContextMenu2.GetCommandString(Cardinal(MakeIntResource(Cmd-1)), GCS_VERB, NIL,PAnsiChar(PChar(Verb)), Length(Verb))
  217. Else
  218. ContextMenu.GetCommandString(Cardinal(MakeIntResource(Cmd-1)),GCS_VERB, NIL, PAnsiChar(PChar(Verb)), Length(Verb));
  219. SetLength(Verb, strlen(PChar(Verb)));
  220. Verb := LowerCase(Verb);
  221. If (Verb <> shcRename) And (PerformPaste Or (Verb <> shcPaste)) Then
  222. Begin
  223. FillChar(ICM,SizeOf(TCMInvokeCommandInfo),#0);
  224. ICM.cbSize := Sizeof(TCMInvokeCommandInfo);
  225. ICM.hwnd := Handle;
  226. ICM.lpVerb := LPCSTR(MakeIntResource(Cmd-1));
  227. ICM.nShow := SW_SHOWNORMAL;
  228. Try
  229. If Assigned(ContextMenu2) Then
  230. ContextMenu2.InvokeCommand(ICM)
  231. Else
  232. ContextMenu.InvokeCommand(ICM);
  233. Except
  234. // eat any dammned shell exceptions!
  235. Exit;
  236. End;
  237. End;
  238. End;
  239. End;
  240. Finally
  241. DestroyMenu(PopupMenu);
  242. If CallbackWnd <> 0 Then
  243. DestroyWindow(CallbackWnd);
  244. IF Assigned(ContextMenu2) Then
  245. ContextMenu2._Release;
  246. IF Assigned(ContextMenu) Then
  247. ContextMenu._Release;
  248. End;
  249. End; {ShellDisplayContextMenu (PIDL) }
  250. Procedure ShellDisplayContextMenu(Handle: THandle;
  251. P: TPoint;
  252. FileName : String;
  253. AllowRename : Boolean;
  254. Var Verb: String;
  255. PerformPaste : Boolean = True);
  256. Var ShellFolder : iShellFolder;
  257. DirPIDL : PItemIDList;
  258. DirPIDLFQ : PItemIDList;
  259. ParentPIDL : PItemIDList;
  260. Begin
  261. DirPIDL := NIL;
  262. DirPIDLFQ := NIL;
  263. ParentPIDL := NIL;
  264. DirPidlFQ := PIDL_GetFromPath(PChar(FileName));
  265. IF Assigned(DirPIDLFQ) Then
  266. Begin
  267. PIDL_GetRelative(DirPIDLFQ, ParentPIDL, DirPIDL);
  268. Try
  269. If PIDL_GetFileFolder(ParentPIDL, ShellFolder) Then
  270. ShellDisplayContextMenu(Handle, P, ShellFolder, 1, DirPIDL, AllowRename, Verb, PerformPaste);
  271. Finally
  272. PIDL_Free(DirPIDL);
  273. PIDL_Free(DirPIDLFQ);
  274. PIDL_Free(ParentPIDL);
  275. End;
  276. End;
  277. End; {ShellDisplayContextMenu (Filename) }
  278. Procedure ShellDisplayContextMenu(Handle: THandle;
  279. P: TPoint;
  280. Path : String;
  281. Files : TStringList;
  282. Var Verb: String;
  283. PerformPaste : Boolean = True);
  284. Var ShellFolder : iShellFolder;
  285. PathPIDL : PItemIDList;
  286. PIDLArray : PPIDLArray;
  287. Index : Integer;
  288. i : Integer;
  289. Begin
  290. IF Files.Count = 0 Then
  291. Exit;
  292. Index := 0;
  293. GetMem(PIDLArray, SizeOf(PItemIDList) * Files.Count);
  294. FillChar(PIDLArray^, Sizeof(PItemIDList) * Files.Count, #0);
  295. Try
  296. PathPIDL := PIDL_GetFromPath(PChar(Path));
  297. IF Assigned(PathPIDL) Then
  298. Begin
  299. Try
  300. If PIDL_GetFileFolder(PathPIDL, ShellFolder) Then
  301. Begin
  302. For i := 0 To Files.Count - 1 Do
  303. Begin
  304. PIDLArray^[i] := PIDL_GetFromParentFolder(ShellFolder, PChar(Files[i]));
  305. IF Assigned(PIDLArray^[i]) Then
  306. INC(Index);
  307. End;
  308. IF Index > 0 Then
  309. Begin
  310. Try
  311. ShellDisplayContextMenu(Handle, P, ShellFolder, Index, PIDLArray^[0], False, Verb, PerformPaste);
  312. Finally
  313. For i := 0 To Index - 1 Do
  314. PIDL_Free(PIDLArray[i]);
  315. End;
  316. End;
  317. End;
  318. Finally
  319. PIDL_Free(PathPIDL);
  320. End;
  321. End;
  322. Finally
  323. FreeMem(PIDLArray);
  324. End;
  325. End; {ShellDisplayContextMenu (TStringList) }
  326. Function ShellExecuteContextCommand(Handle: THandle; Command: String; ShellFolder: IShellFolder; PIDLCount: Integer; Var PIDL: PItemIDList): Boolean;
  327. Var ICM : TCMInvokeCommandInfoEx;
  328. ContextMenu : IContextMenu;
  329. ContextMenu2: IContextMenu2;
  330. Popup : HMenu;
  331. MenuCmd : Cardinal;
  332. HRes : HResult;
  333. Begin
  334. Result := False;
  335. IF Succeeded(ShellFolder.GetUIObjectOf(Handle, PIDLCount, PIDL, IID_IContextMenu, NIL, Pointer(ContextMenu))) Then
  336. Begin
  337. ContextMenu.QueryInterface(IID_IContextMenu2, ContextMenu2);
  338. FillChar(ICM,SizeOf(TCMInvokeCommandInfo), #0);
  339. ICM.hwnd := Handle;
  340. ICM.cbSize := SizeOf(TCMInvokeCommandInfo);
  341. ICM.nShow := SW_SHOWNORMAL;
  342. IF Command <> shcDefault Then
  343. begin
  344. ICM.fMask := CMIC_MASK_UNICODE;
  345. ICM.lpVerb := PAnsiChar(AnsiString(Command));
  346. ICM.lpVerbW := PChar(Command);
  347. end
  348. Else
  349. Begin
  350. {Locate the menuitem for the default action:}
  351. Popup := CreatePopupMenu;
  352. Try
  353. Try
  354. IF Assigned(ContextMenu2) Then
  355. Hres := ContextMenu2.QueryContextMenu(Popup, 0, 1, $7FFF, CMF_DEFAULTONLY)
  356. Else
  357. Hres := ContextMenu.QueryContextMenu(Popup, 0, 1, $7FFF, CMF_DEFAULTONLY);
  358. Except
  359. Exit;
  360. End;
  361. If Succeeded(HRes) Then
  362. Begin
  363. MenuCmd := GetMenuDefaultItem(Popup, 0, 0);
  364. If MenuCmd <> $FFFFFFFF then
  365. ICM.lpVerb := LPCSTR(MakeIntResource(MenuCmd-1))
  366. Else
  367. ICM.lpVerb := NIL;
  368. end;
  369. finally
  370. DestroyMenu(Popup);
  371. end;
  372. End;
  373. Try
  374. If Assigned(ContextMenu2) then
  375. Result := Succeeded(ContextMenu2.InvokeCommand(PCMInvokeCommandInfo(@ICM)^))
  376. Else
  377. Result := Succeeded(ContextMenu.InvokeCommand(PCMInvokeCommandInfo(@ICM)^));
  378. Except
  379. // eat any dammned shell exceptions.
  380. End;
  381. End;
  382. End; {ShellExecuteContextCommand (PIDL)}
  383. Function ShellExecuteContextCommand(Handle: THandle; Command: String; FileName : String): Boolean;
  384. Var ShellFolder : iShellFolder;
  385. DirPIDL : PItemIDList;
  386. DirPIDLFQ : PItemIDList;
  387. ParentPIDL : PItemIDList;
  388. Begin
  389. DirPIDL := NIL;
  390. DirPIDLFQ := NIL;
  391. ParentPIDL := NIL;
  392. Result := False;
  393. DirPidlFQ := PIDL_GetFromPath(PChar(FileName));
  394. IF Assigned(DirPIDLFQ) Then
  395. Begin
  396. PIDL_GetRelative(DirPIDLFQ, ParentPIDL, DirPIDL);
  397. Try
  398. If PIDL_GetFileFolder(ParentPIDL, ShellFolder) Then
  399. Result := ShellExecuteContextCommand(Handle, Command, ShellFolder, 1, DirPIDL);
  400. Finally
  401. PIDL_Free(DirPIDL);
  402. PIDL_Free(DirPIDLFQ);
  403. PIDL_Free(ParentPIDL);
  404. End;
  405. End;
  406. End; {ShellExecuteContextCommand (FileName)}
  407. Function ShellExecuteContextCommand(Handle: THandle;
  408. Command : String;
  409. Path : String;
  410. Files : TStringList) : Boolean;
  411. Var ShellFolder : iShellFolder;
  412. PathPIDL : PItemIDList;
  413. PIDLArray : PPIDLArray;
  414. Index : Integer;
  415. i : Integer;
  416. Begin
  417. Result := False;
  418. IF Files.Count = 0 Then
  419. Exit;
  420. Index := 0;
  421. GetMem(PIDLArray, SizeOf(PItemIDList) * Files.Count);
  422. FillChar(PIDLArray^, Sizeof(PItemIDList) * Files.Count, #0);
  423. Try
  424. PathPIDL := PIDL_GetFromPath(PChar(Path));
  425. IF Assigned(PathPIDL) Then
  426. Begin
  427. Try
  428. If PIDL_GetFileFolder(PathPIDL, ShellFolder) Then
  429. Begin
  430. For i := 0 To Files.Count - 1 Do
  431. Begin
  432. PIDLArray^[i] := PIDL_GetFromParentFolder(ShellFolder, PChar(Files[i]));
  433. IF Assigned(PIDLArray^[i]) Then
  434. INC(Index);
  435. End;
  436. IF Index > 0 Then
  437. Begin
  438. Try
  439. Result := ShellExecuteContextCommand(Handle, Command, ShellFolder, Index, PIDLArray^[0]);
  440. Finally
  441. For i := 0 To Index - 1 Do
  442. PIDL_Free(PIDLArray^[i]);
  443. End;
  444. End;
  445. End;
  446. Finally
  447. PIDL_Free(PathPIDL);
  448. End;
  449. End;
  450. Finally
  451. FreeMem(PIDLArray);
  452. End;
  453. End; {ShellExecuteContextCommand (TStringList) }
  454. End.