ShellDialogs.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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), http://www.gexperts.com
  21. and
  22. Brad Stowers (ItemProp), http://www.delphifreestuff.com.
  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,PChar(Verb), Length(Verb))
  217. Else
  218. ContextMenu.GetCommandString(Cardinal(MakeIntResource(Cmd-1)),GCS_VERB, NIL, 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 := 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 : TCMInvokeCommandInfo;
  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. ICM.lpVerb := PChar(Command)
  344. Else
  345. Begin
  346. {Locate the menuitem for the default action:}
  347. Popup := CreatePopupMenu;
  348. Try
  349. Try
  350. IF Assigned(ContextMenu2) Then
  351. Hres := ContextMenu2.QueryContextMenu(Popup, 0, 1, $7FFF, CMF_DEFAULTONLY)
  352. Else
  353. Hres := ContextMenu.QueryContextMenu(Popup, 0, 1, $7FFF, CMF_DEFAULTONLY);
  354. Except
  355. Exit;
  356. End;
  357. If Succeeded(HRes) Then
  358. Begin
  359. MenuCmd := GetMenuDefaultItem(Popup, 0, 0);
  360. If MenuCmd <> $FFFFFFFF then
  361. ICM.lpVerb := MakeIntResource(MenuCmd-1)
  362. Else
  363. ICM.lpVerb := NIL;
  364. end;
  365. finally
  366. DestroyMenu(Popup);
  367. end;
  368. End;
  369. Try
  370. If Assigned(ContextMenu2) then
  371. Result := Succeeded(ContextMenu2.InvokeCommand(ICM))
  372. Else
  373. Result := Succeeded(ContextMenu.InvokeCommand(ICM));
  374. Except
  375. // eat any dammned shell exceptions.
  376. End;
  377. End;
  378. End; {ShellExecuteContextCommand (PIDL)}
  379. Function ShellExecuteContextCommand(Handle: THandle; Command: String; FileName : String): Boolean;
  380. Var ShellFolder : iShellFolder;
  381. DirPIDL : PItemIDList;
  382. DirPIDLFQ : PItemIDList;
  383. ParentPIDL : PItemIDList;
  384. Begin
  385. DirPIDL := NIL;
  386. DirPIDLFQ := NIL;
  387. ParentPIDL := NIL;
  388. Result := False;
  389. DirPidlFQ := PIDL_GetFromPath(PChar(FileName));
  390. IF Assigned(DirPIDLFQ) Then
  391. Begin
  392. PIDL_GetRelative(DirPIDLFQ, ParentPIDL, DirPIDL);
  393. Try
  394. If PIDL_GetFileFolder(ParentPIDL, ShellFolder) Then
  395. Result := ShellExecuteContextCommand(Handle, Command, ShellFolder, 1, DirPIDL);
  396. Finally
  397. PIDL_Free(DirPIDL);
  398. PIDL_Free(DirPIDLFQ);
  399. PIDL_Free(ParentPIDL);
  400. End;
  401. End;
  402. End; {ShellExecuteContextCommand (FileName)}
  403. Function ShellExecuteContextCommand(Handle: THandle;
  404. Command : String;
  405. Path : String;
  406. Files : TStringList) : Boolean;
  407. Var ShellFolder : iShellFolder;
  408. PathPIDL : PItemIDList;
  409. PIDLArray : PPIDLArray;
  410. Index : Integer;
  411. i : Integer;
  412. Begin
  413. Result := False;
  414. IF Files.Count = 0 Then
  415. Exit;
  416. Index := 0;
  417. GetMem(PIDLArray, SizeOf(PItemIDList) * Files.Count);
  418. FillChar(PIDLArray^, Sizeof(PItemIDList) * Files.Count, #0);
  419. Try
  420. PathPIDL := PIDL_GetFromPath(PChar(Path));
  421. IF Assigned(PathPIDL) Then
  422. Begin
  423. Try
  424. If PIDL_GetFileFolder(PathPIDL, ShellFolder) Then
  425. Begin
  426. For i := 0 To Files.Count - 1 Do
  427. Begin
  428. PIDLArray^[i] := PIDL_GetFromParentFolder(ShellFolder, PChar(Files[i]));
  429. IF Assigned(PIDLArray^[i]) Then
  430. INC(Index);
  431. End;
  432. IF Index > 0 Then
  433. Begin
  434. Try
  435. Result := ShellExecuteContextCommand(Handle, Command, ShellFolder, Index, PIDLArray^[0]);
  436. Finally
  437. For i := 0 To Index - 1 Do
  438. PIDL_Free(PIDLArray^[i]);
  439. End;
  440. End;
  441. End;
  442. Finally
  443. PIDL_Free(PathPIDL);
  444. End;
  445. End;
  446. Finally
  447. FreeMem(PIDLArray);
  448. End;
  449. End; {ShellExecuteContextCommand (TStringList) }
  450. End.