DragDropFilesEx.pas 35 KB

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