DragDropFilesEx.pas 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  1. unit DragDropFilesEx;
  2. {
  3. Description
  4. ===========
  5. TDragDropFilesEx is a comfortable and powerful component for COM/OLE
  6. drag&drop operations with files and supports completely the namespace of
  7. Windows (PIDL). The component is a child-class from TDragDrop.
  8. Disclaimer
  9. ==========
  10. The author disclaims all warranties, expressed or implied, including,
  11. without limitation, the warranties of merchantability and of fitness
  12. for any purpose. The author assumes no liability for damages, direct or
  13. consequential, which may result from the use of this component/unit.
  14. Restrictions on Using the Unit / Components
  15. ===========================================
  16. This unit/component is copyright 1998 by Dieter Steinwedel. ALL RIGHTS
  17. ARE RESERVED BY DIETER STEINWEDEL. You are allowed to use it freely
  18. subject to the following restrictions:
  19. • You are not allowed delete or alter the author's name and
  20. copyright in any manner
  21. • You are not allowed to publish a copy, modified version or
  22. compilation neither for payment in any kind nor freely
  23. • You are allowed to create a link to the download in the WWW
  24. • These restrictions and terms apply to you as long as until
  25. I alter them. Changes can found on my homepage
  26. }
  27. {$ALIGN ON}
  28. {$ASSERTIONS OFF}
  29. {$BOOLEVAL OFF}
  30. {$DENYPACKAGEUNIT OFF}
  31. {$EXTENDEDSYNTAX ON}
  32. {$HINTS ON}
  33. {$IMPORTEDDATA ON}
  34. {$LONGSTRINGS ON}
  35. {$OPTIMIZATION ON}
  36. {$TYPEDADDRESS OFF}
  37. {$TYPEINFO OFF}
  38. {$WARNINGS ON}
  39. interface
  40. uses
  41. DragDrop, Windows, Classes, SysUtils, ActiveX, PIDL, ShlObj, ComObj, Registry;
  42. type
  43. PDropFiles = ^TDropFiles;
  44. TDropFiles = packed record
  45. pFiles: DWORD; { offset of file list }
  46. pt: TPoint; { drop point (client coords) }
  47. fNC: BOOL; { is it on NonClient area }
  48. fWide: BOOL; { WIDE character switch }
  49. end;
  50. PItemIDList = ShlObj.PItemIDList;
  51. TFileExMustDnD = (nvFilename, nvPIDL);
  52. TFileExMustDnDSet = set of TFileExMustDnD;
  53. TOnSpecifyDropTarget =
  54. procedure(Sender: TObject; DragDropHandler: Boolean; pt: TPoint; var pidlFQ: PItemIDList; var Filename: string) of object;
  55. PFDDListItem = ^TFDDListItem;
  56. TFDDListItem = record
  57. pidlFQ: PItemIDList;
  58. Name: string;
  59. MappedName: string;
  60. end;
  61. PCMListItem = ^TCMListItem;
  62. TCMListItem = record
  63. FirstCmd: Integer;
  64. LastCmd: Integer;
  65. CM: IContextMenu;
  66. end;
  67. TFileList = class(TList)
  68. private
  69. function Get(Index: Integer): PFDDListItem;
  70. procedure Put(Index: Integer; Item: PFDDListItem);
  71. public
  72. constructor Create;
  73. destructor Destroy; override;
  74. procedure Clear; override;
  75. procedure Delete(Index: Integer);
  76. function Remove(Item: PFDDListItem): Integer;
  77. function First: PFDDListItem;
  78. function Last: PFDDListItem;
  79. function AddItem(ApidlFQ: PItemIDList; AName: string):Integer;
  80. function AddItemEx(ApidlFQ: PItemIDList; AName, AMappedName: string): Integer;
  81. function RenderPIDLs: Boolean;
  82. function RenderNames: Boolean;
  83. property Items[Index: Integer]: PFDDListItem read Get write Put;
  84. end;
  85. TDataObjectFilesEx = class(TDataObject)
  86. private
  87. pidlStream: TMemoryStream;
  88. HDropStream: TMemoryStream;
  89. FilenameMapList: TStringList;
  90. FilenamesAreMapped: Boolean;
  91. FOnRelease: TNotifyEvent;
  92. FPreferCopy: Boolean;
  93. public
  94. constructor Create(AFileList: TFileList; RenderPIDL, RenderFilename, PreferCopy: Boolean);
  95. destructor Destroy; override;
  96. function RenderData(FormatEtc: TFormatEtc; var StgMedium: TStgMedium): HResult; override;
  97. function IsValid(FormatPidl, FormatHDrop: Boolean): Boolean;
  98. property OnRelease: TNotifyEvent read FOnRelease write FOnRelease;
  99. end;
  100. TDropTargetFilesEx = class(TDropTarget)
  101. protected
  102. procedure AcceptDataObject(DataObj: IDataObject; var Accept: Boolean); override;
  103. public
  104. constructor Create(AOwner: TDragDrop);
  105. destructor Destroy; override;
  106. procedure RenderDropped(DataObj: IDataObject; grfKeyState: LongInt; pt: TPoint; var dwEffect: LongInt); override;
  107. end;
  108. TShellExtension = class(TPersistent)
  109. private
  110. FDropHandler: Boolean;
  111. FDragDropHandler: Boolean;
  112. protected
  113. procedure AssignTo(Dest: TPersistent); override;
  114. published
  115. property DropHandler: Boolean read FDropHandler write FDropHandler default False;
  116. property DragDropHandler: Boolean read FDragDropHandler write FDragDropHandler default False;
  117. end;
  118. TDragDropFilesEx = class(TDragDrop)
  119. private
  120. FFileList: TFileList;
  121. FNeedValid: TFileExMustDnDSet;
  122. FCompleteFileList: Boolean;
  123. FFileNamesAreMapped: Boolean;
  124. FOnSpecifyDropTarget: TOnSpecifyDropTarget;
  125. FShellExtension: TShellExtension;
  126. FCMList: TList;
  127. FOnDataObjectRelease: TNotifyEvent;
  128. FPreferCopy: Boolean;
  129. protected
  130. function CreateDataObject:TDataObject; override;
  131. procedure DataObjectRelease(Sender: TObject);
  132. procedure DoMenuPopup(Sender: TObject; AMenu: HMenu; DataObj: IDataObject;
  133. AMinCustCmd: Integer; grfKeyState: LongInt; pt: TPoint); override;
  134. function DoMenuExecCmd(Sender: TObject; AMenu: HMenu; DataObj: IDataObject;
  135. Command: Integer; var dwEffect: LongInt): Boolean; override;
  136. procedure DoMenuDestroy(Sender: TObject; AMenu: HMenu); override;
  137. function DropHandler(const dataObj: IDataObject; grfKeyState: LongInt; pt: TPoint; var dwEffect: LongInt): Boolean; override;
  138. public
  139. constructor Create(AOwner: TComponent); override;
  140. destructor Destroy; override;
  141. function TargetHasDropHandler(pidlFQ: PItemIDList; Filename: string; var dwEffect: LongInt): Boolean;
  142. property FileList: TFileList read FFileList write FFileList;
  143. property FileNamesAreMapped: Boolean read FFileNamesAreMapped;
  144. property PreferCopy: Boolean read FPreferCopy write FPreferCopy;
  145. published
  146. property NeedValid: TFileExMustDnDSet read FNeedValid write FNeedValid;
  147. property CompleteFileList: Boolean read FCompleteFileList write FCompleteFileList default True;
  148. property ShellExtensions: TShellExtension read FShellExtension write FShellExtension;
  149. property OnSpecifyDropTarget: TOnSpecifyDropTarget read FOnSpecifyDropTarget write FOnSpecifyDropTarget;
  150. property OnDropHandlerSucceeded;
  151. property OnDataObjectRelease: TNotifyEvent read FOnDataObjectRelease write FOnDataObjectRelease;
  152. end;
  153. procedure Register;
  154. implementation
  155. uses
  156. Types;
  157. const
  158. {$EXTERNALSYM IID_IDropTarget}
  159. IID_IDropTarget: TGUID = (
  160. D1:$00000122;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));
  161. type
  162. PByte = ^Byte;
  163. // some local functions --------------------------------------------------------
  164. procedure CopyHDropToFilelist(var List: TFileList; HDropPtr: PAnsiChar; HDropSize: LongInt);
  165. var
  166. s: string;
  167. DropFiles: PDropFiles;
  168. ws: WideString;
  169. // List must be empty, before calling ...
  170. begin
  171. if (HDropPtr <> nil) and (HDropSize > 0) then
  172. begin
  173. PAnsiChar(DropFiles) := HDropPtr;
  174. Inc(HDropPtr,DropFiles^.pFiles);
  175. if DropFiles^.FWide then
  176. begin
  177. while HDropPtr^ <> #0 do
  178. begin
  179. ws := PWideChar(HDropPtr);
  180. Inc(HDropPtr, (Length(ws) + 1) * 2);
  181. List.AddItem(nil, ws);
  182. end;
  183. end
  184. else
  185. begin
  186. while HDropPtr^ <> #0 do
  187. begin
  188. s := string(HDropPtr);
  189. Inc(HDropPtr, Length(s) + 1);
  190. List.AddItem(nil, s);
  191. end;
  192. end;
  193. end;
  194. end;
  195. procedure CopyFilenameMapToFilelist(
  196. var List: TFileList; FilenameMapPtr: PChar; FilenameMapSize: LongInt; IsWideChar: Boolean);
  197. var
  198. s: string;
  199. idx: LongInt;
  200. ws: WideString;
  201. // should be only called after "CopyHDropToFilelist" ...
  202. begin
  203. if (FilenameMapPtr<>nil) and (FilenameMapSize>0) then
  204. begin
  205. idx := 0;
  206. if IsWideChar then
  207. begin
  208. while FilenameMapPtr^ <> #0 do
  209. begin
  210. ws := WideCharToString(PWideChar(FilenameMapPtr));
  211. Inc(FilenameMapPtr, (Length(ws) + 1) * 2);
  212. if Idx >= 0 then List.Items[Idx]^.MappedName := ws
  213. else raise Exception.Create('A non-existing filename is mapped');
  214. Inc(Idx);
  215. end;
  216. end
  217. else
  218. begin
  219. while FilenameMapPtr^ <> #0 do
  220. begin
  221. s := StrPas(FilenameMapPtr);
  222. Inc(FilenameMapPtr, Length(s) + 1);
  223. if Idx >= 0 then List.Items[Idx]^.MappedName := s
  224. else raise Exception.Create('A non-existing filename is mapped');
  225. Inc(Idx);
  226. end;
  227. end;
  228. end;
  229. end;
  230. procedure CopyPIDLsToFilelist(var List: TFileList; pidlPtr: PByte; pidlSize: LongInt);
  231. var
  232. size,i,Idx,Count: LongInt;
  233. pidl, pidlRoot: PItemIDList;
  234. LIPtr: ^LongInt;
  235. AddToList: Boolean;
  236. begin
  237. if (pidlPtr <> nil) and (pidlSize > 0) then
  238. begin
  239. PByte(LIPtr) := pidlPtr;
  240. count := LIPtr^;
  241. AddToList := (List.Count = 0);
  242. Idx := 0;
  243. Inc(LIPtr);
  244. Inc(pidlPtr, LIPtr^);
  245. i := LIPtr^; // mempos
  246. pidlRoot := nil;
  247. Inc(LIPtr);
  248. while (Count > Idx) and (i < pidlSize) do
  249. begin
  250. PByte(pidl) := pidlPtr;
  251. size := PIDL_GetSize(pidl);
  252. if i = LIPtr^ then
  253. begin // is an item pidl ...
  254. if AddToList then List.AddItem(PIDL_Concatenate(pidlRoot, pidl), '')
  255. // PIDL_Concatenate --> waste of memory
  256. else List.Items[Idx]^.pidlFQ := PIDL_Concatenate(pidlRoot, pidl);
  257. Inc(Idx);
  258. Inc(LIPtr);
  259. end
  260. else pidlRoot := pidl; // is a root pidl ...
  261. Inc(i, size);
  262. Inc(pidlPtr, size);
  263. end;
  264. end;
  265. end;
  266. // TFileList -------------------------------------------------------------------
  267. constructor TFileList.Create;
  268. begin
  269. inherited Create;
  270. end;
  271. destructor TFileList.Destroy;
  272. begin
  273. Clear;
  274. inherited Destroy;
  275. end;
  276. function TFileList.Get(Index: Integer): PFDDListItem;
  277. begin
  278. Result := inherited Items[Index];
  279. end;
  280. procedure TFileList.Put(Index: Integer; Item: PFDDListItem);
  281. begin
  282. inherited Items[Index] := Item;
  283. end;
  284. procedure TFileList.Clear;
  285. var
  286. Item: PFDDListItem;
  287. i: Integer;
  288. begin
  289. if Count > 0 then
  290. begin
  291. for i:=0 to Count-1 do
  292. begin
  293. Item := inherited Items[i];
  294. if Item <> nil then
  295. begin
  296. PIDL_Free(Item^.pidlFQ);
  297. Dispose(Item);
  298. end;
  299. end;
  300. end;
  301. inherited Clear;
  302. end;
  303. procedure TFileList.Delete(Index: Integer);
  304. var
  305. Item: PFDDListItem;
  306. begin
  307. Item := inherited Items[Index];
  308. if Item <> nil then
  309. begin
  310. PIDL_Free(Item^.pidlFQ);
  311. Dispose(Item);
  312. end;
  313. inherited Delete(Index);
  314. end;
  315. function TFileList.Remove(Item: PFDDListItem): Integer;
  316. begin
  317. Result := inherited Remove(Item);
  318. if Item <> nil then
  319. begin
  320. PIDL_Free(Item^.pidlFQ);
  321. Dispose(Item);
  322. end;
  323. end;
  324. function TFileList.First: PFDDListItem;
  325. begin
  326. Result := inherited First;
  327. end;
  328. function TFileList.Last: PFDDListItem;
  329. begin
  330. Result := inherited Last;
  331. end;
  332. function TFileList.AddItem(ApidlFQ: PItemIDList; AName: string): Integer;
  333. var
  334. LI: PFDDListItem;
  335. begin
  336. New(LI);
  337. LI^.Name := AName;
  338. LI^.MappedName := '';
  339. LI^.pidlFQ := PIDL_Copy(ApidlFQ);
  340. Result := Add(LI);
  341. end;
  342. function TFileList.AddItemEx(ApidlFQ:PItemIDList; AName, AMappedName: string): Integer;
  343. var
  344. LI: PFDDListItem;
  345. begin
  346. New(LI);
  347. LI^.Name := AName;
  348. LI^.MappedName := AMappedName;
  349. LI^.pidlFQ := PIDL_Copy(ApidlFQ);
  350. Result := Add(LI);
  351. end;
  352. function TFileList.RenderPIDLs:Boolean;
  353. var
  354. i: Integer;
  355. piDesktop: IShellFolder;
  356. olePath: WideString;
  357. ulEaten, ulAttribs: ULong;
  358. begin
  359. if Failed(SHGetDesktopFolder(piDesktop)) then
  360. begin
  361. Result := False;
  362. end
  363. else
  364. begin
  365. Result := True;
  366. if Count > 0 then
  367. begin
  368. for i:=0 to Count-1 do
  369. begin
  370. if (Items[i] <> nil) and (Items[i]^.pidlFQ = nil) then
  371. begin
  372. if Items[i]^.Name = '' then Result := False
  373. else
  374. begin
  375. olePath := Items[i]^.Name;
  376. ulAttribs := 0;
  377. if Failed(piDesktop.ParseDisplayName(0, nil, POleStr(olePath), ulEaten, Items[i]^.pidlFQ, ulAttribs)) then
  378. begin
  379. Result := False;
  380. end;
  381. end;
  382. end;
  383. end;
  384. end;
  385. end;
  386. end;
  387. function TFileList.RenderNames: Boolean;
  388. var
  389. i: Integer;
  390. SF: IShellFolder;
  391. pc: array[0..1024] of char;
  392. ppidlRoot, ppidlItem: PItemIDList;
  393. begin
  394. Result:=True;
  395. if Count>0 then
  396. begin
  397. for i:=0 to Count-1 do
  398. begin
  399. if (Items[i] <> nil) and (Items[i]^.Name = '') then
  400. begin
  401. if Items[i]^.pidlFQ = nil then Result := False
  402. else
  403. begin
  404. PIDL_GetRelative(Items[i]^.pidlFQ, ppidlRoot, ppidlItem);
  405. if PIDL_GetFileFolder(ppidlRoot, SF) then
  406. begin
  407. if PIDL_GetDisplayName(SF, ppidlItem, SHGDN_FORPARSING, pc, SizeOf(pc)) then Items[i]^.Name := StrPas(pc)
  408. else
  409. begin
  410. Items[i]^.Name := '';
  411. Result := False;
  412. end;
  413. PIDL_Free(ppidlRoot);
  414. PIDL_Free(ppidlItem);
  415. end
  416. else Result := False;
  417. end;
  418. end;
  419. end;
  420. end;
  421. end;
  422. // TDataObjectFilesEx -------------------------------------------------------------
  423. constructor TDataObjectFilesEx.Create(AFileList: TFileList; RenderPIDL, RenderFilename, PreferCopy: Boolean);
  424. var
  425. i: DWORD;
  426. FE: TFormatEtc;
  427. SM: TStgMedium;
  428. LastpidlRoot, pidlRoot, pidlItem: PItemIDList;
  429. Pos: DWORD;
  430. df: TDropFiles;
  431. pc: array[0..1024] of Char;
  432. begin
  433. inherited Create;
  434. pidlStream := TMemoryStream.Create;
  435. HDropStream := TMemoryStream.Create;
  436. FilenameMapList := TStringList.Create;
  437. FilenamesAreMapped := False;
  438. if RenderPIDL then
  439. begin
  440. LastpidlRoot := nil;
  441. pidlStream.SetSize(AFileList.Count * 4 + 8);
  442. pidlStream.Seek(0, 0);
  443. i := AFileList.Count;
  444. pidlStream.Write(i, 4);
  445. i := pidlStream.Size;
  446. pidlStream.Write(i, 4);
  447. pidlStream.Seek(0, 2);
  448. for i := 0 to AFileList.Count - 1 do
  449. begin
  450. if AFileList.Items[i]^.pidlFQ = nil then
  451. begin
  452. pidlStream.SetSize(0);
  453. break;
  454. end;
  455. PIDL_GetRelative(AFileList.Items[i]^.pidlFQ, pidlRoot, pidlItem);
  456. if (LastpidlRoot = nil) or (not PIDL_Equal(LastpidlRoot,pidlRoot)) then
  457. begin
  458. if LastpidlRoot <> nil then PIDL_Free(LastpidlRoot);
  459. LastpidlRoot := PIDL_Copy(pidlRoot);
  460. pidlStream.Write(pidlRoot^, PIDL_GetSize(pidlRoot));
  461. end;
  462. pos := pidlStream.Position;
  463. pidlStream.Write(pidlItem^, PIDL_GetSize(pidlItem));
  464. pidlStream.seek(8 + 4 * i, soBeginning);
  465. pidlStream.Write(pos, 4);
  466. pidlStream.Seek(0, 2);
  467. PIDL_Free(pidlRoot);
  468. PIDL_Free(pidlItem);
  469. end;
  470. PIDL_Free(LastpidlRoot);
  471. if pidlStream.Size <> 0 then
  472. begin
  473. with FE do
  474. begin
  475. cfFormat := CF_SHELLIDLIST;
  476. ptd := nil;
  477. dwAspect := DVASPECT_CONTENT;
  478. lindex := -1;
  479. tymed := TYMED_HGLOBAL;
  480. end;
  481. SetData(FE, SM, False);
  482. end;
  483. end;
  484. if RenderFilename then
  485. begin
  486. with df do
  487. begin
  488. pfiles := SizeOf(TDropFiles);
  489. pt.x := 0;
  490. pt.y := 0;
  491. LongInt(fnc) := 0;
  492. LongInt(Fwide) := 1;
  493. end;
  494. HDropStream.Write(df, SizeOf(df));
  495. for i := 0 to AFileList.Count - 1 do
  496. begin
  497. if AFileList.Items[i]^.Name='' then
  498. begin
  499. HDropStream.SetSize(0);
  500. break;
  501. end;
  502. strPcopy(pc, AFileList.Items[i]^.Name + #0);
  503. HDropStream.Write(pc, (Length(AFileList.Items[i]^.Name) + 1) * SizeOf(pc[0]));
  504. FilenameMapList.Add(AFileList.Items[i]^.MappedName);
  505. if FilenameMapList[i]<>'' then FilenamesAreMapped := True;
  506. end;
  507. if HDropStream.Size <> 0 then
  508. begin
  509. with FE do
  510. begin
  511. cfFormat := CF_HDROP;
  512. ptd := nil;
  513. dwAspect := DVASPECT_CONTENT;
  514. lindex := -1;
  515. tymed := TYMED_HGLOBAL;
  516. end;
  517. SetData(FE, SM, False);
  518. pc[0] := #0;
  519. HDropStream.Write(pc, SizeOf(pc[0]));
  520. end;
  521. if FilenamesAreMapped then
  522. begin
  523. with FE do
  524. begin
  525. cfFormat := CF_FILENAMEMAPW;
  526. ptd := nil;
  527. dwAspect := DVASPECT_CONTENT;
  528. lindex := -1;
  529. tymed := TYMED_HGLOBAL;
  530. end;
  531. SetData(FE, SM, False);
  532. end;
  533. end;
  534. FPreferCopy := PreferCopy;
  535. if PreferCopy then
  536. begin
  537. with FE do
  538. begin
  539. cfFormat := CF_PREFERREDDROPEFFECT;
  540. ptd := nil;
  541. dwAspect := DVASPECT_CONTENT;
  542. lindex := -1;
  543. tymed := TYMED_HGLOBAL;
  544. end;
  545. SetData(FE, SM, False);
  546. end;
  547. end;
  548. destructor TDataObjectFilesEx.Destroy;
  549. begin
  550. if Assigned(OnRelease) then OnRelease(Self);
  551. pidlStream.Free;
  552. HDropStream.Free;
  553. FilenameMapList.Free;
  554. inherited Destroy;
  555. end;
  556. function TDataObjectFilesEx.RenderData(FormatEtc: TFormatEtc; var StgMedium: TStgMedium): HResult;
  557. var
  558. h: HGlobal;
  559. p: Pointer;
  560. FilenameMapStream: TMemoryStream;
  561. i: Integer;
  562. pc: array[0..1024] of Char;
  563. begin
  564. Result := E_FAIL;
  565. if FormatEtc.cfFormat = CF_SHELLIDLIST then
  566. begin
  567. h := GlobalAlloc(GHND or GMEM_SHARE, pidlStream.Size);
  568. if h = 0 then
  569. begin
  570. Result := E_OUTOFMEMORY;
  571. Exit;
  572. end;
  573. p := GlobalLock(h);
  574. pidlStream.Seek(0,0);
  575. pidlStream.Read(p^, pidlStream.Size);
  576. GlobalUnlock(h);
  577. with StgMedium do
  578. begin
  579. tymed := TYMED_HGLOBAL;
  580. hGlobal := h;
  581. unkForRelease := nil;
  582. end;
  583. Result := S_OK;
  584. end;
  585. if FormatEtc.cfFormat = CF_HDROP then
  586. begin
  587. h := GlobalAlloc(GHND or GMEM_SHARE, HDropStream.Size);
  588. if h = 0 then
  589. begin
  590. Result := E_OUTOFMEMORY;
  591. Exit;
  592. end;
  593. p := GlobalLock(h);
  594. HDropStream.Seek(0,0);
  595. HDropStream.Read(p^, HDropStream.Size);
  596. GlobalUnlock(h);
  597. with StgMedium do
  598. begin
  599. tymed :=TYMED_HGLOBAL;
  600. hGlobal := h;
  601. unkForRelease := nil;
  602. end;
  603. Result := S_OK;
  604. end;
  605. if (FormatEtc.cfFormat = CF_FILENAMEMAP) or (FormatEtc.cfFormat = CF_FILENAMEMAPW) then
  606. begin
  607. FilenameMapStream := TMemoryStream.Create;
  608. if (FormatEtc.cfFormat=CF_FILENAMEMAPW) then
  609. begin
  610. for i := 0 to FilenameMapList.Count - 1 do
  611. begin
  612. StringToWideChar(FilenameMapList[i], PWideChar(@pc), SizeOf(pc));
  613. FilenameMapStream.Write(pc, Length(WideString(PWideChar(@pc))) * 2 + 2);
  614. end;
  615. pc[0]:=#0;
  616. pc[1]:=#0;
  617. FilenameMapStream.Write(pc, 2);
  618. end
  619. else
  620. begin
  621. for i := 0 to FilenameMapList.count-1 do
  622. begin
  623. strPcopy(pc,FilenameMapList[i] + #0);
  624. FilenameMapStream.Write(pc, Length(FilenameMapList[i]) + 1);
  625. end;
  626. pc[0] := #0;
  627. FilenameMapStream.Write(pc, 1);
  628. end;
  629. h := GlobalAlloc(GHND or GMEM_SHARE, FilenameMapStream.Size);
  630. if h = 0 then
  631. begin
  632. Result := E_OUTOFMEMORY;
  633. FilenameMapStream.Free;
  634. Exit;
  635. end;
  636. p := GlobalLock(h);
  637. FilenameMapStream.Seek(0,0);
  638. FilenameMapStream.Read(p^, FilenameMapStream.Size);
  639. FilenameMapStream.Free;
  640. GlobalUnlock(h);
  641. with StgMedium do
  642. begin
  643. tymed := TYMED_HGLOBAL;
  644. hGlobal := h;
  645. unkForRelease := nil;
  646. end;
  647. Result := S_OK;
  648. end;
  649. if (FormatEtc.cfFormat = CF_PREFERREDDROPEFFECT) and FPreferCopy then
  650. begin
  651. h := GlobalAlloc(GHND or GMEM_SHARE, SizeOf(DWORD));
  652. if h = 0 then
  653. begin
  654. Result := E_OUTOFMEMORY;
  655. Exit;
  656. end;
  657. p := GlobalLock(h);
  658. PDWORD(p)^ := DROPEFFECT_COPY;
  659. GlobalUnlock(h);
  660. with StgMedium do
  661. begin
  662. tymed := TYMED_HGLOBAL;
  663. hGlobal := h;
  664. unkForRelease := nil;
  665. end;
  666. Result := S_OK;
  667. end;
  668. end;
  669. function TDataObjectFilesEx.IsValid(FormatPidl, FormatHDrop: Boolean): Boolean;
  670. begin
  671. Result:= not ((FormatPidl and (pidlStream.Size = 0)) or (FormatHDrop and (HDropStream.Size = 0)));
  672. end;
  673. // TDropTargetFilesEx -------------------------------------------------------------
  674. constructor TDropTargetFilesEx.Create(AOwner: TDragDrop);
  675. begin
  676. inherited Create(AOwner);
  677. end;
  678. destructor TDropTargetFilesEx.Destroy;
  679. begin
  680. inherited Destroy;
  681. end;
  682. procedure TDropTargetFilesEx.AccepTDataObject(DataObj: IDataObject; var Accept: Boolean);
  683. var
  684. FE: TFormatEtc;
  685. HasHDrop, HasIDList: Boolean;
  686. begin
  687. Accept := False;
  688. with FE do
  689. begin
  690. cfFormat := CF_HDROP;
  691. ptd := nil;
  692. dwAspect := DVASPECT_CONTENT;
  693. lindex := -1;
  694. tymed := TYMED_HGLOBAL;
  695. end;
  696. HasHDrop := (DataObj.QueryGetData(FE) = S_OK);
  697. if HasHDrop or (not (nvFilename in TDragDropFilesEx(FOwner).FNeedValid)) then
  698. begin
  699. with FE do
  700. begin
  701. cfFormat := CF_SHELLIDLIST;
  702. ptd := nil;
  703. dwAspect := DVASPECT_CONTENT;
  704. lindex := -1;
  705. tymed := TYMED_HGLOBAL;
  706. end;
  707. HasIDList := (DataObj.QueryGetData(FE) = S_OK);
  708. if HasIDList or (not (nvPIDL in TDragDropFilesEx(FOwner).FNeedValid)) then
  709. begin
  710. Accept := HasIDList or HasHDrop;
  711. end;
  712. end;
  713. end;
  714. procedure TDropTargetFilesEx.RenderDropped(
  715. DataObj: IDataObject; grfKeyState: LongInt; pt: TPoint; var dwEffect: LongInt);
  716. var
  717. FormatEtc: TFormatEtc;
  718. StgMedium: TStgMedium;
  719. HDropSize, pidlSize, FileNameMapSize: LongInt;
  720. HDropPtr, pidlPtr, FileNameMapPtr: Pointer;
  721. HDropHandle,pidlHandle, FileNameMapHandle: THandle;
  722. IsWideChar: Boolean;
  723. begin
  724. TDragDropFilesEx(FOwner).FFileList.Clear;
  725. // get "CF_HDROP" items
  726. with FormatEtc do
  727. begin
  728. cfFormat := CF_HDROP;
  729. ptd := nil;
  730. dwAspect := DVASPECT_CONTENT;
  731. lindex := -1;
  732. tymed := TYMED_HGLOBAL;
  733. end;
  734. if DataObj.GetData(FormatEtc, StgMedium) = S_OK then HDropHandle := StgMedium.HGlobal
  735. else HDropHandle := 0;
  736. if HDropHandle <> 0 then
  737. begin
  738. try
  739. HDropSize := GlobalSize(HDropHandle);
  740. HDropPtr := GlobalLock(HDropHandle);
  741. CopyHDropToFilelist(TDragDropFilesEx(FOwner).FFileList, HDropPtr, HDropSize);
  742. finally
  743. GlobalUnLock(HDropHandle);
  744. ReleaseStgMedium(StgMedium);
  745. end;
  746. // CF_FILENAMEMAP makes only sense if CF_HDROP exists ...
  747. // get "CF_FILENAMEMAP" or "CF_FILENAMEMAPW" items
  748. with FormatEtc do
  749. begin
  750. cfFormat := CF_FILENAMEMAP;
  751. ptd := nil;
  752. dwAspect := DVASPECT_CONTENT;
  753. lindex := -1;
  754. tymed := TYMED_HGLOBAL;
  755. end;
  756. IsWideChar := False;
  757. FileNameMapHandle := 0;
  758. if DataObj.GetData(FormatEtc, StgMedium) = S_OK then FileNameMapHandle := StgMedium.HGlobal
  759. else
  760. begin
  761. with FormatEtc do
  762. begin
  763. cfFormat := CF_FILENAMEMAPW;
  764. ptd := nil;
  765. dwAspect := DVASPECT_CONTENT;
  766. lindex := -1;
  767. tymed := TYMED_HGLOBAL;
  768. end;
  769. if DataObj.GetData(FormatEtc, StgMedium) = S_OK then
  770. begin
  771. FileNameMapHandle := StgMedium.HGlobal;
  772. IsWideChar := True;
  773. end;
  774. end;
  775. if FileNameMapHandle <> 0 then
  776. begin
  777. TDragDropFilesEx(FOwner).FFileNamesAreMapped := True;
  778. try
  779. FileNameMapSize := GlobalSize(FileNameMapHandle);
  780. FileNameMapPtr := GlobalLock(FileNameMapHandle);
  781. CopyFileNameMapToFilelist(TDragDropFilesEx(FOwner).FFileList, FileNameMapPtr, FileNameMapSize, IsWideChar);
  782. finally
  783. GlobalUnLock(FileNameMapHandle);
  784. ReleaseStgMedium(StgMedium);
  785. end;
  786. end
  787. else TDragDropFilesEx(FOwner).FFileNamesAreMapped := False;
  788. end
  789. else TDragDropFilesEx(FOwner).FFileNamesAreMapped := False;
  790. // get "CF_SHELLIDLIST" items
  791. with FormatEtc do
  792. begin
  793. cfFormat := CF_SHELLIDLIST;
  794. ptd := nil;
  795. dwAspect := DVASPECT_CONTENT;
  796. lindex := -1;
  797. tymed := TYMED_HGLOBAL;
  798. end;
  799. if DataObj.GetData(FormatEtc, StgMedium) = S_OK then pidlHandle := StgMedium.HGlobal
  800. else pidlHandle := 0;
  801. if pidlHandle <> 0 then
  802. begin
  803. try
  804. pidlSize := GlobalSize(pidlHandle);
  805. PidlPtr := GlobalLock(pidlHandle);
  806. CopyPIDLsToFilelist(TDragDropFilesEx(FOwner).FFileList, pidlPtr, pidlSize);
  807. finally
  808. GlobalUnLock(pidlHandle);
  809. ReleaseStgMedium(StgMedium);
  810. end;
  811. end;
  812. end;
  813. // TShellExtension ---------------------------------------------------
  814. procedure TShellExtension.AssignTo(Dest: TPersistent);
  815. begin
  816. if Dest is TShellExtension then
  817. begin
  818. with TShellExtension(Dest) do
  819. begin
  820. FDropHandler := Self.FDropHandler;
  821. FDragDropHandler := Self.FDragDropHandler;
  822. end;
  823. end
  824. else inherited AssignTo(Dest);
  825. end;
  826. // TDragDropFilesEx ---------------------------------------------------------------
  827. constructor TDragDropFilesEx.Create(AOwner: TComponent);
  828. begin
  829. inherited Create(AOwner);
  830. FFileList := TFileList.Create;
  831. FDropTarget._Release;
  832. FDropTarget := TDropTargetFilesEx.Create(self);
  833. FCompleteFileList := True;
  834. SourceCompatibility := [];
  835. FFileNamesAreMapped := False;
  836. FCMList := TList.Create;
  837. FShellExtension := TShellExtension.Create;
  838. FPreferCopy := False;
  839. end;
  840. destructor TDragDropFilesEx.Destroy;
  841. begin
  842. FCMList.Free;
  843. FFileList.Free;
  844. FShellExtension.Free;
  845. inherited;
  846. end;
  847. procedure TDragDropFilesEx.DataObjectRelease(Sender: TObject);
  848. begin
  849. if Assigned(OnDataObjectRelease) then OnDataObjectRelease(Self);
  850. end;
  851. function TDragDropFilesEx.CreateDataObject: TDataObject;
  852. var
  853. DataObject: TDataObjectFilesEx;
  854. RFName, RPidl: Boolean;
  855. begin
  856. Result := nil;
  857. if FCompleteFileList then
  858. begin
  859. RFName := FFileList.RenderNames;
  860. RPidl := FFileList.RenderPIDLs;
  861. if ((nvFilename in FNeedValid) and (not RFName)) or
  862. ((nvPIDL in FNeedValid) and (not RPidl)) then
  863. begin
  864. exit;
  865. end;
  866. end
  867. else
  868. begin
  869. RFName := True;
  870. RPidl := True;
  871. end;
  872. if FFileList.Count > 0 then
  873. begin
  874. DataObject := TDataObjectFilesEx.Create(FFileList, RPidl, RFname, FPreferCopy);
  875. DataObject.OnRelease := DataObjectRelease;
  876. if not DataObject.IsValid((nvPIDL in FNeedValid), (nvFilename in FNeedValid)) then DataObject._Release
  877. else Result := DataObject;
  878. end;
  879. end;
  880. procedure TDragDropFilesEx.DoMenuPopup(
  881. Sender: TObject; AMenu: HMenu; DataObj: IDataObject; AMinCustCmd:Integer; grfKeyState: LongInt; pt: TPoint);
  882. var
  883. StringList: TStringList;
  884. Reg: TRegistry;
  885. pidlFQ: PItemIDList;
  886. FileName: string;
  887. procedure CreateDragDropHandler(GUID:string);
  888. var
  889. Unknown: IUnknown;
  890. ShellExtInit: IShellExtInit;
  891. CMListItem: PCMListItem;
  892. begin
  893. try
  894. Unknown := CreateComObject(StringToGUID(GUID));
  895. except
  896. Unknown := nil;
  897. end;
  898. try
  899. if Assigned(Unknown) and
  900. (Unknown.QueryInterface(IID_IShellExtInit, ShellExtInit) = S_OK) then
  901. begin
  902. if ShellExtInit.Initialize(pidlFQ, DataObj, 0) = NoError then
  903. begin
  904. New(CMListItem);
  905. if ShellExtInit.QueryInterface(IID_IContextMenu, CMListItem^.CM) = S_OK then
  906. begin
  907. CMListItem^.FirstCmd := AMinCustCmd;
  908. CMListItem^.LastCmd := AMinCustCmd;
  909. Inc(CMListItem^.LastCmd, CMListItem^.CM.QueryContextMenu(AMenu, 0, CMListItem^.FirstCmd, $7FFF, CMF_NORMAL));
  910. if CMListItem^.LastCmd = CMListItem^.FirstCmd then Dispose(CMListItem)
  911. else
  912. begin
  913. AMinCustCmd := CMListItem^.LastCmd;
  914. FCMList.Add(CMListItem);
  915. end;
  916. end;
  917. end;
  918. end;
  919. finally
  920. Unknown := nil;
  921. ShellExtInit := nil;
  922. end;
  923. end;
  924. begin
  925. if Assigned(FOnSpecifyDropTarget) and FShellExtension.FDragDropHandler then
  926. begin
  927. pidlFQ := nil;
  928. FOnSpecifyDropTarget(self, True, DragDropControl.ScreenToClient(pt),
  929. pidlFQ, Filename);
  930. if pidlFQ=nil then pidlFQ:=PIDL_GetFromPath(PChar(Filename))
  931. else pidlFQ:=PIDL_Copy(pidlFQ);
  932. StringList:=TStringList.Create;
  933. Reg:=TRegistry.Create;
  934. try
  935. Reg.RootKey := HKEY_CLASSES_ROOT;
  936. if Reg.OpenKey('Folder\ShellEx\DragDropHandlers', False) then
  937. begin
  938. Reg.GetKeyNames(StringList);
  939. Reg.CloseKey;
  940. while StringList.Count>0 do
  941. begin
  942. { The documentation for the drag-and-drop handlers varies
  943. between many registry-keys, where you find the handlers.
  944. I think, the correct position is "Folder"; "Directory"
  945. should be the key for system-folders! Even, I have found
  946. in the documentation, that you can define drag-and-drop
  947. handlers for the system-folder "printers". Till now, it
  948. doesn't make sense to me. Therefore, I haven't implemented
  949. it }
  950. if Reg.OpenKey('Folder\ShellEx\DragDropHandlers\' + StringList[StringList.Count - 1], False) then
  951. begin
  952. CreateDragDropHandler(Reg.ReadString(''));
  953. Reg.CloseKey;
  954. end;
  955. StringList.Delete(StringList.Count - 1);
  956. end;
  957. end;
  958. finally
  959. Stringlist.Free;
  960. Reg.Free;
  961. PIDL_Free(pidlFQ);
  962. end;
  963. end;
  964. inherited DoMenuPopup(Sender, AMenu, DataObj, AMinCustCmd, grfKeyState, pt);
  965. end;
  966. function TDragDropFilesEx.DoMenuExecCmd(
  967. Sender: TObject; AMenu: HMenu; DataObj: IDataObject; Command: Integer; var dwEffect: LongInt): Boolean;
  968. var
  969. ICM: TCMInvokeCommandInfo;
  970. i: Integer;
  971. CMListItem: PCMListItem;
  972. begin
  973. Result := False;
  974. try
  975. if FCMList.Count>0 then
  976. begin
  977. for i := 0 to FCMList.Count-1 do
  978. begin
  979. CMListItem := FCMList.Items[i];
  980. if (CMListItem^.FirstCmd <= Command) and
  981. (CMListItem^.LastCmd > Command) then
  982. begin
  983. FillChar(ICM, SizeOf(TCMInvokeCommandInfo), #0);
  984. ICM.cbSize := SizeOf(TCMInvokeCommandInfo);
  985. ICM.hwnd := DragDropControl.Handle;
  986. ICM.lpVerb := MakeIntResourceA(Command-CMListItem^.FirstCmd);
  987. ICM.nShow := SW_SHOWNORMAL;
  988. Result := (CMListItem^.CM.InvokeCommand(ICM) = NOERROR);
  989. break;
  990. end;
  991. end;
  992. end;
  993. finally
  994. if not Result then
  995. begin
  996. Result := inherited DoMenuExecCmd(Sender, AMenu, DataObj, Command, dwEffect);
  997. end;
  998. end;
  999. end;
  1000. procedure TDragDropFilesEx.DoMenuDestroy(Sender: TObject; AMenu: HMenu);
  1001. var
  1002. CMListItem: PCMListItem;
  1003. begin
  1004. while FCMList.Count > 0 do
  1005. begin
  1006. CMListItem := FCMList.Items[FCMList.Count - 1];
  1007. CMListItem^.CM := nil;
  1008. Dispose(CMListItem);
  1009. FCMList.Delete(FCMList.Count - 1);
  1010. end;
  1011. inherited DoMenuDestroy(Sender, AMenu);
  1012. end;
  1013. function TDragDropFilesEx.TargetHasDropHandler(
  1014. pidlFQ: PItemIDList; Filename: string; var dwEffect: LongInt): Boolean;
  1015. var
  1016. ppidlFQ, ppidlRoot, ppidlItem: PItemIDList;
  1017. SF: IShellFolder;
  1018. DT: IDropTarget;
  1019. begin
  1020. try
  1021. Result := False;
  1022. ppidlFQ := nil;
  1023. ppidlRoot := nil;
  1024. ppidlItem := nil;
  1025. if pidlFQ = nil then ppidlFQ := PIDL_GetFromPath(PChar(Filename))
  1026. else ppidlFQ := PIDL_Copy(pidlFQ);
  1027. PIDL_GetRelative(ppidlFQ, ppidlRoot, ppidlItem);
  1028. PIDL_GetFileFolder(ppidlRoot, SF);
  1029. if Assigned(SF) then
  1030. begin
  1031. SF.GetUIObjectOf(0,1,ppidlItem,IID_IDropTarget,nil,Pointer(DT));
  1032. Result := Assigned(DT) and FileExists(Filename);
  1033. if Assigned(DT) and (dwEffect and not (DROPEFFECT_SCROLL or DROPEFFECT_LINK) = DROPEFFECT_NONE) then
  1034. begin
  1035. dwEffect := dwEffect and not DROPEFFECT_LINK;
  1036. end;
  1037. end;
  1038. finally
  1039. SF := nil;
  1040. DT := nil;
  1041. PIDL_Free(ppidlRoot);
  1042. PIDL_Free(ppidlItem);
  1043. PIDL_Free(ppidlFQ);
  1044. end;
  1045. end;
  1046. function TDragDropFilesEx.DropHandler(
  1047. const dataObj: IDataObject; grfKeyState: LongInt; pt: TPoint; var dwEffect: LongInt): Boolean;
  1048. var
  1049. pidlFQ, ppidlFQ, ppidlRoot, ppidlItem: PItemIDList;
  1050. SF: IShellFolder;
  1051. DT: IDropTarget;
  1052. Filename: string;
  1053. pc: array[0..1024] of Char;
  1054. begin
  1055. try
  1056. Result := False;
  1057. ppidlRoot := nil;
  1058. ppidlItem := nil;
  1059. ppidlFQ := nil;
  1060. if ShellExtensions.FDropHandler and Assigned(FOnSpecifyDropTarget) then
  1061. begin
  1062. pidlFQ := nil;
  1063. FOnSpecifyDropTarget(Self, False, DragDropControl.ScreenToClient(pt), pidlFQ, Filename);
  1064. if pidlFQ = nil then ppidlFQ := PIDL_GetFromPath(PChar(Filename))
  1065. else ppidlFQ := PIDL_Copy(pidlFQ);
  1066. PIDL_GetRelative(ppidlFQ, ppidlRoot, ppidlItem);
  1067. PIDL_GetFileFolder(ppidlRoot, SF);
  1068. if Assigned(SF) then
  1069. begin
  1070. if (Filename = '') and
  1071. PIDL_GetDisplayName(SF, ppidlItem, SHGDN_FORPARSING, pc, SizeOf(pc)) then
  1072. begin
  1073. Filename := StrPas(pc);
  1074. end;
  1075. SF.GetUIObjectOf(0, 1, ppidlItem, IID_IDropTarget, nil, Pointer(DT));
  1076. if FileExists(Filename) and Assigned(DT) then
  1077. begin
  1078. DT.DragEnter(DataObj, grfKeyState, pt, dwEffect);
  1079. Result := (DT.Drop(DataObj, grfKeyState, pt, dwEffect) = NOERROR);
  1080. end;
  1081. end;
  1082. end;
  1083. finally
  1084. SF := nil;
  1085. DT := nil;
  1086. PIDL_Free(ppidlRoot);
  1087. PIDL_Free(ppidlItem);
  1088. PIDL_Free(ppidlFQ);
  1089. end;
  1090. end;
  1091. // Register Component ----------------------------------------------------------
  1092. procedure Register;
  1093. begin
  1094. RegisterComponents('DragDrop', [TDragDropFilesEx]);
  1095. end;
  1096. end.