DragDropFilesEx.pas 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105
  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. const
  159. {$EXTERNALSYM IID_IDropTarget}
  160. IID_IDropTarget: TGUID = (
  161. D1:$00000122;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));
  162. type PByte=^byte;
  163. // some local functions --------------------------------------------------------
  164. procedure CopyHDropToFilelist(var List:TFileList; HDropPtr:PChar; HDropSize:longint);
  165. var s:string;
  166. DropFiles: PDropFiles;
  167. // List must be empty, before calling ...
  168. begin
  169. if (HDropPtr<>nil) and (HDropSize>0) then
  170. begin
  171. PChar(DropFiles):=HDropPtr;
  172. inc(HDropPtr,DropFiles^.pFiles);
  173. if DropFiles^.FWide then
  174. begin
  175. while HDropPtr^<>#0 do
  176. begin
  177. s:=WideCharToString(PWideChar(HDropPtr));
  178. inc(HDropPtr,(Length(s)+1)*2);
  179. List.AddItem(nil,s);
  180. end;
  181. end
  182. else
  183. begin
  184. while HDropPtr^<>#0 do
  185. begin
  186. s:=StrPas(HDropPtr);
  187. inc(HDropPtr, Length(s)+1);
  188. List.AddItem(nil,s);
  189. end;
  190. end;
  191. end;
  192. end;
  193. procedure CopyFilenameMapToFilelist(var List:TFileList; FilenameMapPtr:PChar;
  194. FilenameMapSize:longint; IsWideChar:boolean);
  195. var s:string;
  196. idx:longint;
  197. // should be only called after "CopyHDropToFilelist" ...
  198. begin
  199. if (FilenameMapPtr<>nil) and (FilenameMapSize>0) then
  200. begin
  201. idx:=0;
  202. if IsWideChar then
  203. begin
  204. while FilenameMapPtr^<>#0 do
  205. begin
  206. s:=WideCharToString(PWideChar(FilenameMapPtr));
  207. inc(FilenameMapPtr,(Length(s)+1)*2);
  208. if Idx>=0 then List.Items[Idx]^.MappedName:=s
  209. else raise Exception.Create('A non-existing filename is mapped');
  210. inc(Idx);
  211. end;
  212. end
  213. else
  214. begin
  215. while FilenameMapPtr^<>#0 do
  216. begin
  217. s:=StrPas(FilenameMapPtr);
  218. inc(FilenameMapPtr, Length(s)+1);
  219. if Idx>=0 then List.Items[Idx]^.MappedName:=s
  220. else raise Exception.Create('A non-existing filename is mapped');
  221. inc(Idx);
  222. end;
  223. end;
  224. end;
  225. end;
  226. procedure CopyPIDLsToFilelist(var List:TFileList; pidlPtr:PByte; pidlSize:longint);
  227. var size,i,Idx,Count:longint;
  228. pidl, pidlRoot:PItemIDList;
  229. LIPtr:^longint;
  230. AddToList:boolean;
  231. begin
  232. if (pidlPtr<>nil) and (pidlSize>0) then
  233. begin
  234. PByte(LIPtr):=pidlPtr;
  235. count:=LIPtr^;
  236. AddToList:=List.Count=0;
  237. Idx:=0;
  238. inc(LIPtr);
  239. inc(pidlPtr,LIPtr^);
  240. i:=LIPtr^; // mempos
  241. pidlRoot:=nil;
  242. inc(LIPtr);
  243. while (Count>Idx) and (i<pidlSize) do
  244. begin
  245. PByte(pidl):=pidlPtr;
  246. size:=PIDL_GetSize(pidl);
  247. if i=LIPtr^ then
  248. begin // is an item pidl ...
  249. if AddToList then
  250. List.AddItem(PIDL_Concatenate(pidlRoot,pidl),'')
  251. // PIDL_Concatenate --> waste of memory!!!
  252. else List.Items[Idx]^.pidlFQ:=PIDL_Concatenate(pidlRoot,pidl);
  253. inc(Idx);
  254. inc(LIPtr);
  255. end
  256. else pidlRoot:=pidl; // is a root pidl ...
  257. inc(i,size);
  258. inc(pidlPtr,size);
  259. end;
  260. end;
  261. end;
  262. // TFileList -------------------------------------------------------------------
  263. constructor TFileList.Create;
  264. begin
  265. inherited Create;
  266. end;
  267. destructor TFileList.Destroy;
  268. begin
  269. Clear;
  270. inherited Destroy;
  271. end;
  272. function TFileList.Get(Index: Integer): PFDDListItem;
  273. begin
  274. Result:=inherited Items[Index];
  275. end;
  276. procedure TFileList.Put(Index: Integer; Item: PFDDListItem);
  277. begin
  278. inherited Items[Index]:=Item;
  279. end;
  280. procedure TFileList.Clear;
  281. var Item:PFDDListItem;
  282. i:integer;
  283. begin
  284. if Count>0 then
  285. for i:=0 to Count-1 do
  286. begin
  287. Item:=inherited Items[i];
  288. if Item<>nil then
  289. begin
  290. PIDL_Free(Item^.pidlFQ);
  291. dispose(Item);
  292. end;
  293. end;
  294. inherited Clear;
  295. end;
  296. procedure TFileList.Delete(Index:Integer);
  297. var Item:PFDDListItem;
  298. begin
  299. Item:=inherited Items[Index];
  300. if Item<>nil then
  301. begin
  302. PIDL_Free(Item^.pidlFQ);
  303. dispose(Item);
  304. end;
  305. inherited Delete(Index);
  306. end;
  307. function TFileList.Remove(Item: PFDDListItem): Integer;
  308. begin
  309. Result:=inherited Remove(Item);
  310. if Item<>nil then
  311. begin
  312. PIDL_Free(Item^.pidlFQ);
  313. dispose(Item);
  314. end;
  315. end;
  316. function TFileList.First: PFDDListItem;
  317. begin
  318. Result:=inherited First;
  319. end;
  320. function TFileList.Last: PFDDListItem;
  321. begin
  322. Result:=inherited Last;
  323. end;
  324. function TFileList.AddItem(ApidlFQ:PItemIDList; AName:String):integer;
  325. var LI:PFDDListItem;
  326. begin
  327. New(LI);
  328. LI^.Name:=AName;
  329. LI^.MappedName:='';
  330. LI^.pidlFQ:=PIDL_Copy(ApidlFQ);
  331. Result:=Add(LI);
  332. end;
  333. function TFileList.AddItemEx(ApidlFQ:PItemIDList; AName, AMappedName:String):integer;
  334. var LI:PFDDListItem;
  335. begin
  336. New(LI);
  337. LI^.Name:=AName;
  338. LI^.MappedName:=AMappedName;
  339. LI^.pidlFQ:=PIDL_Copy(ApidlFQ);
  340. Result:=Add(LI);
  341. end;
  342. function TFileList.RenderPIDLs:boolean;
  343. var i:integer;
  344. piDesktop: IShellFolder;
  345. olePath: array[1..Max_Path] of TOleChar;
  346. ulEaten, ulAttribs:ULong;
  347. begin
  348. if Failed(SHGetDesktopFolder(piDesktop)) then
  349. begin
  350. Result:=false;
  351. exit;
  352. end;
  353. Result:=true;
  354. if Count>0 then
  355. for i:=0 to Count-1 do
  356. if (Items[i]<>nil) and (Items[i]^.pidlFQ=nil) then
  357. begin
  358. if Items[i]^.Name='' then Result:=false
  359. else
  360. begin
  361. MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
  362. PChar(Items[i]^.Name), -1, @olePath, sizeof(olepath));
  363. if Failed(piDesktop.ParseDisplayName(0, nil, @OlePath, ulEaten,
  364. Items[i]^.pidlFQ, ulAttribs)) then Result:=false;
  365. end;
  366. end;
  367. end;
  368. function TFileList.RenderNames:boolean;
  369. var i:integer;
  370. SF:IShellFolder;
  371. pc:array[0..1024] of char;
  372. ppidlRoot, ppidlItem: PItemIDList;
  373. begin
  374. Result:=true;
  375. if Count>0 then
  376. for i:=0 to Count-1 do
  377. if (Items[i]<>nil) and (Items[i]^.Name='') then
  378. begin
  379. if Items[i]^.pidlFQ=nil then Result:=false
  380. else
  381. begin
  382. PIDL_GetRelative(Items[i]^.pidlFQ, ppidlRoot, ppidlItem);
  383. if PIDL_GetFileFolder(ppidlRoot,SF) then
  384. begin
  385. if PIDL_GetDisplayName(SF, ppidlItem,
  386. SHGDN_FORPARSING,pc,sizeof(pc)) then
  387. Items[i]^.Name:=StrPas(pc)
  388. else
  389. begin
  390. Items[i]^.Name:='';
  391. Result:=false;
  392. end;
  393. PIDL_Free(ppidlRoot);
  394. PIDL_Free(ppidlItem);
  395. end
  396. else Result:=false;
  397. end;
  398. end;
  399. end;
  400. // TDataObjectFilesEx -------------------------------------------------------------
  401. constructor TDataObjectFilesEx.Create(AFileList:TFileList; RenderPIDL, RenderFilename: boolean);
  402. var i:DWORD;
  403. FE:TFormatEtc;
  404. SM:TStgMedium;
  405. LastpidlRoot, pidlRoot, pidlItem:PItemIDList;
  406. Pos:DWORD;
  407. df:TDropFiles;
  408. pc:array[0..1024] of char;
  409. begin
  410. inherited Create;
  411. pidlStream:=TMemoryStream.Create;
  412. HDropStream:=TMemoryStream.Create;
  413. FilenameMapList:=TStringList.Create;
  414. FilenamesAreMapped:=false;
  415. if RenderPIDL then
  416. begin
  417. LastpidlRoot:=nil;
  418. pidlStream.SetSize(AFileList.count*4+8);
  419. pidlStream.Seek(0,0);
  420. i:=AFileList.count;
  421. pidlStream.Write(i,4);
  422. i:=pidlStream.Size;
  423. pidlStream.Write(i,4);
  424. pidlStream.Seek(0,2);
  425. for i:=0 to AFileList.count-1 do
  426. begin
  427. if AFileList.Items[i]^.pidlFQ=nil then
  428. begin
  429. pidlStream.SetSize(0);
  430. break;
  431. end;
  432. PIDL_GetRelative(AFileList.Items[i]^.pidlFQ,
  433. pidlRoot, pidlItem);
  434. if (LastpidlRoot=nil) or (PIDL_Equal(LastpidlRoot,pidlRoot)=false) then
  435. begin
  436. if LastpidlRoot<>nil then PIDL_Free(LastpidlRoot);
  437. LastpidlRoot:=PIDL_Copy(pidlRoot);
  438. pidlStream.Write(pidlRoot^,PIDL_GetSize(pidlRoot));
  439. end;
  440. pos:=pidlStream.Position;
  441. pidlStream.Write(pidlItem^,PIDL_GetSize(pidlItem));
  442. pidlStream.seek(8+4*i,0);
  443. pidlStream.Write(pos,4);
  444. pidlStream.Seek(0,2);
  445. PIDL_Free(pidlRoot);
  446. PIDL_Free(pidlItem);
  447. end;
  448. PIDL_Free(LastpidlRoot);
  449. if pidlStream.Size<>0 then
  450. begin
  451. with FE do
  452. begin
  453. cfFormat:=CF_SHELLIDLIST;
  454. ptd:=nil;
  455. dwAspect:=DVASPECT_CONTENT;
  456. lindex:=-1;
  457. tymed:=TYMED_HGLOBAL;
  458. end;
  459. SetData(FE,SM,false);
  460. end;
  461. end;
  462. if RenderFilename then
  463. begin
  464. with df do
  465. begin
  466. pfiles:=sizeof(Tdropfiles);
  467. pt.x:=0;
  468. pt.y:=0;
  469. longint(fnc) := 0;
  470. longint(Fwide) := 0;
  471. end;
  472. HDropStream.Write(df,sizeof(df));
  473. for i:=0 to AFileList.count-1 do
  474. begin
  475. if AFileList.Items[i]^.Name='' then
  476. begin
  477. HDropStream.SetSize(0);
  478. break;
  479. end;
  480. strPcopy(pc,AFileList.Items[i]^.Name+#0);
  481. HDropStream.Write(pc,length(AFileList.Items[i]^.Name)+1);
  482. FilenameMapList.Add(AFileList.Items[i]^.MappedName);
  483. if FilenameMapList[i]<>'' then FilenamesAreMapped:=true;
  484. end;
  485. if HDropStream.Size<>0 then
  486. begin
  487. with FE do
  488. begin
  489. cfFormat:=cf_HDrop;
  490. ptd:=nil;
  491. dwAspect:=DVAspect_Content;
  492. lindex:=-1;
  493. tymed:=tymed_HGlobal;
  494. end;
  495. SetData(FE,SM,false);
  496. pc[0]:=#0;
  497. HDropStream.Write(pc,1);
  498. end;
  499. if FilenamesAreMapped then
  500. begin
  501. if Win32Platform=VER_PLATFORM_WIN32_WINDOWS then
  502. begin
  503. with FE do
  504. begin
  505. cfFormat:=CF_FILENAMEMAP;
  506. ptd:=nil;
  507. dwAspect:=DVASPECT_CONTENT;
  508. lindex:=-1;
  509. tymed:=TYMED_HGLOBAL;
  510. end;
  511. SetData(FE,SM,false);
  512. end
  513. else
  514. begin
  515. with FE do
  516. begin
  517. cfFormat:=CF_FILENAMEMAPW;
  518. ptd:=nil;
  519. dwAspect:=DVASPECT_CONTENT;
  520. lindex:=-1;
  521. tymed:=TYMED_HGLOBAL;
  522. end;
  523. SetData(FE,SM,false);
  524. end;
  525. end;
  526. end;
  527. end;
  528. destructor TDataObjectFilesEx.Destroy;
  529. begin
  530. pidlStream.Free;
  531. HDropStream.Free;
  532. FilenameMapList.Free;
  533. inherited Destroy;
  534. end;
  535. function TDataObjectFilesEx.RenderData(FormatEtc:TFormatEtc;
  536. var StgMedium: TStgMedium):HResult;
  537. var h: HGlobal;
  538. p:pointer;
  539. FilenameMapStream:TMemoryStream;
  540. i:integer;
  541. pc:array[0..1024] of char;
  542. begin
  543. Result:=E_Fail;
  544. if FormatEtc.cfFormat=CF_SHELLIDLIST then
  545. begin
  546. h := GlobalAlloc(GHND or GMEM_SHARE, pidlStream.Size);
  547. if h = 0 then
  548. begin
  549. Result:= E_OUTOFMEMORY;
  550. Exit;
  551. end;
  552. p:=globallock(h);
  553. pidlStream.Seek(0,0);
  554. pidlStream.Read(p^,pidlStream.Size);
  555. globalunlock(h);
  556. with StgMedium do
  557. begin
  558. tymed:=TYMED_HGLOBAL;
  559. hGlobal := h;
  560. unkForRelease := nil;
  561. end;
  562. result:=S_OK;
  563. end;
  564. if FormatEtc.cfFormat=cf_HDrop then
  565. begin
  566. h := GlobalAlloc(GHND or GMEM_SHARE, HDropStream.Size);
  567. if h = 0 then
  568. begin
  569. Result:= E_OUTOFMEMORY;
  570. Exit;
  571. end;
  572. p:=globallock(h);
  573. HDropStream.Seek(0,0);
  574. HDropStream.Read(p^,HDropStream.Size);
  575. globalunlock(h);
  576. with StgMedium do
  577. begin
  578. tymed:=TYMED_HGLOBAL;
  579. hGlobal := h;
  580. unkForRelease := nil;
  581. end;
  582. Result:=S_OK;
  583. end;
  584. if (FormatEtc.cfFormat=CF_FILENAMEMAP) or (FormatEtc.cfFormat=CF_FILENAMEMAPW) then
  585. begin
  586. FilenameMapStream:=TMemoryStream.Create;
  587. if (FormatEtc.cfFormat=CF_FILENAMEMAPW) then
  588. begin
  589. for i:=0 to FilenameMapList.count-1 do
  590. begin
  591. StringToWideChar(FilenameMapList[i],PWideChar(@pc),sizeof(pc));
  592. FilenameMapStream.Write(pc,length(FilenameMapList[i])*2+2);
  593. end;
  594. pc[0]:=#0; pc[1]:=#0;
  595. FilenameMapStream.Write(pc,2);
  596. end
  597. else
  598. begin
  599. for i:=0 to FilenameMapList.count-1 do
  600. begin
  601. strPcopy(pc,FilenameMapList[i]+#0);
  602. FilenameMapStream.Write(pc,length(FilenameMapList[i])+1);
  603. end;
  604. pc[0]:=#0;
  605. FilenameMapStream.Write(pc,1);
  606. end;
  607. h := GlobalAlloc(GHND or GMEM_SHARE, FilenameMapStream.Size);
  608. if h = 0 then
  609. begin
  610. Result:= E_OUTOFMEMORY;
  611. FilenameMapStream.Free;
  612. Exit;
  613. end;
  614. p:=globallock(h);
  615. FilenameMapStream.Seek(0,0);
  616. FilenameMapStream.Read(p^,FilenameMapStream.Size);
  617. FilenameMapStream.Free;
  618. globalunlock(h);
  619. with StgMedium do
  620. begin
  621. tymed:=TYMED_HGLOBAL;
  622. hGlobal := h;
  623. unkForRelease := nil;
  624. end;
  625. result:=S_OK;
  626. end;
  627. end;
  628. function TDataObjectFilesEx.IsValid(Formatpidl, FormatHDrop:boolean):boolean;
  629. begin
  630. Result:= not ((Formatpidl and (pidlStream.Size=0)) or (FormatHDrop and (HDropStream.Size=0)));
  631. end;
  632. // TDropTargetFilesEx -------------------------------------------------------------
  633. constructor TDropTargetFilesEx.Create(AOwner: TDragDrop);
  634. begin
  635. inherited Create(AOwner);
  636. end;
  637. destructor TDropTargetFilesEx.Destroy;
  638. begin
  639. inherited Destroy;
  640. end;
  641. procedure TDropTargetFilesEx.AccepTDataObject(DataObj: IDataObject;
  642. var Accept:boolean);
  643. var FE:TFormatEtc;
  644. HasHDrop, HasIDList:boolean;
  645. begin
  646. Accept:=false;
  647. with FE do
  648. begin
  649. cfFormat:=cf_HDrop;
  650. ptd:=nil;
  651. dwAspect:=DVASPECT_CONTENT;
  652. lindex:=-1;
  653. tymed:=TYMED_HGLOBAL;
  654. end;
  655. HasHDrop:=DataObj.QueryGetData(FE)=S_OK;
  656. if (HasHDrop=false) and (nvFilename in TDragDropFilesEx(FOwner).FNeedValid) then exit;
  657. with FE do
  658. begin
  659. cfFormat:=CF_SHELLIDLIST;
  660. ptd:=nil;
  661. dwAspect:=DVASPECT_CONTENT;
  662. lindex:=-1;
  663. tymed:=TYMED_HGLOBAL;
  664. end;
  665. HasIDList:=DataObj.QueryGetData(FE)=S_OK;
  666. if (HasIDList=false) and (nvPIDL in TDragDropFilesEx(FOwner).FNeedValid) then exit;
  667. Accept:=HasIDList or HasHDrop;
  668. end;
  669. procedure TDropTargetFilesEx.RenderDropped(DataObj: IDataObject; grfKeyState: Longint;
  670. pt: TPoint; var dwEffect: longint);
  671. var FormatEtc: TFormatEtc;
  672. StgMedium: TStgMedium;
  673. HDropSize, pidlSize, FileNameMapSize: longint;
  674. HDropPtr, pidlPtr, FileNameMapPtr: pointer;
  675. HDropHandle,pidlHandle, FileNameMapHandle: THandle;
  676. IsWideChar:boolean;
  677. begin
  678. TDragDropFilesEx(FOwner).FFileList.Clear;
  679. // get "cf_HDrop" items
  680. with FormatEtc do
  681. begin
  682. cfFormat:=cf_HDrop;
  683. ptd:=nil;
  684. dwAspect:=DVASPECT_CONTENT;
  685. lindex:=-1;
  686. tymed:=TYMED_HGLOBAL;
  687. end;
  688. if DataObj.GetData(FormatEtc, StgMedium)=S_Ok then HDropHandle:=StgMedium.HGlobal
  689. else HDropHandle:=0;
  690. if HDropHandle<>0 then
  691. begin
  692. try
  693. HDropSize:=GlobalSize(HDropHandle);
  694. HDropPtr:=GlobalLock(HDropHandle);
  695. CopyHDropToFilelist(TDragDropFilesEx(FOwner).FFileList,
  696. HDropPtr, HDropSize);
  697. finally
  698. GlobalUnLock(HDropHandle);
  699. ReleaseStgMedium(StgMedium);
  700. end;
  701. // CF_FILENAMEMAP makes only sense if cf_HDrop exists ...
  702. // get "CF_FILENAMEMAP" or "CF_FILENAMEMAPW" items
  703. with FormatEtc do
  704. begin
  705. cfFormat:=CF_FILENAMEMAP;
  706. ptd:=nil;
  707. dwAspect:=DVASPECT_CONTENT;
  708. lindex:=-1;
  709. tymed:=TYMED_HGLOBAL;
  710. end;
  711. IsWideChar:=false;
  712. FileNameMapHandle:=0;
  713. if DataObj.GetData(FormatEtc, StgMedium)=S_Ok then FileNameMapHandle:=StgMedium.HGlobal
  714. else
  715. begin
  716. with FormatEtc do
  717. begin
  718. cfFormat:=CF_FILENAMEMAPW;
  719. ptd:=nil;
  720. dwAspect:=DVASPECT_CONTENT;
  721. lindex:=-1;
  722. tymed:=TYMED_HGLOBAL;
  723. end;
  724. if DataObj.GetData(FormatEtc, StgMedium)=S_Ok then
  725. begin
  726. FileNameMapHandle:=StgMedium.HGlobal;
  727. IsWideChar:=true;
  728. end;
  729. end;
  730. if FileNameMapHandle<>0 then
  731. begin
  732. TDragDropFilesEx(FOwner).FFileNamesAreMapped:=true;
  733. try
  734. FileNameMapSize:=GlobalSize(FileNameMapHandle);
  735. FileNameMapPtr:=GlobalLock(FileNameMapHandle);
  736. CopyFileNameMapToFilelist(TDragDropFilesEx(FOwner).FFileList,
  737. FileNameMapPtr, FileNameMapSize,IsWideChar);
  738. finally
  739. GlobalUnLock(FileNameMapHandle);
  740. ReleaseStgMedium(StgMedium);
  741. end;
  742. end
  743. else TDragDropFilesEx(FOwner).FFileNamesAreMapped:=false;
  744. end
  745. else TDragDropFilesEx(FOwner).FFileNamesAreMapped:=false;
  746. // get "CF_SHELLIDLIST" items
  747. with FormatEtc do
  748. begin
  749. cfFormat:=CF_SHELLIDLIST;
  750. ptd:=nil;
  751. dwAspect:=DVASPECT_CONTENT;
  752. lindex:=-1;
  753. tymed:=TYMED_HGLOBAL;
  754. end;
  755. if DataObj.GetData(FormatEtc, StgMedium)=S_Ok then pidlHandle:=StgMedium.HGlobal
  756. else pidlHandle:=0;
  757. if pidlHandle<>0 then
  758. begin
  759. try
  760. pidlSize:=GlobalSize(pidlHandle);
  761. PidlPtr:=GlobalLock(pidlHandle);
  762. CopyPIDLsToFilelist(TDragDropFilesEx(FOwner).FFileList,
  763. pidlPtr, pidlSize);
  764. finally
  765. GlobalUnLock(pidlHandle);
  766. ReleaseStgMedium(StgMedium);
  767. end;
  768. end;
  769. end;
  770. // TShellExtension ---------------------------------------------------
  771. procedure TShellExtension.AssignTo(Dest: TPersistent);
  772. begin
  773. if Dest is TShellExtension then
  774. with TShellExtension(Dest) do
  775. begin
  776. FDropHandler:=Self.FDropHandler;
  777. FDragDropHandler:=Self.FDragDropHandler;
  778. end
  779. else inherited AssignTo(Dest);
  780. end;
  781. // TDragDropFilesEx ---------------------------------------------------------------
  782. constructor TDragDropFilesEx.Create(AOwner: TComponent);
  783. begin
  784. inherited Create(AOwner);
  785. FFileList:=TFileList.Create;
  786. FDropTarget._Release;
  787. FDropTarget:=TDropTargetFilesEx.Create(self);
  788. FCompleteFileList:=true;
  789. SourceCompatibility:=[];
  790. FFileNamesAreMapped:=false;
  791. FCMList:=TList.Create;
  792. FShellExtension:=TShellExtension.Create;
  793. end;
  794. destructor TDragDropFilesEx.Destroy;
  795. begin
  796. FCMList.Free;
  797. FFileList.Free;
  798. FShellExtension.Free;
  799. inherited destroy;
  800. end;
  801. function TDragDropFilesEx.CreateDataObject:TDataObject;
  802. var DataObject:TDataObjectFilesEx;
  803. RFName,RPidl:boolean;
  804. begin
  805. Result:=nil;
  806. if FCompleteFileList then
  807. begin
  808. RFName:=FFileList.RenderNames;
  809. RPidl:=FFileList.RenderPIDLs;
  810. if ((nvFilename in FNeedValid) and (RFName=false)) or
  811. ((nvPIDL in FNeedValid) and (RPidl=false)) then exit;
  812. end
  813. else
  814. begin
  815. RFName:=true;
  816. RPidl:=true;
  817. end;
  818. if FFileList.Count>0 then
  819. begin
  820. DataObject:=TDataObjectFilesEx.Create(FFileList, RPidl, RFname);
  821. if DataObject.IsValid((nvPIDL in FNeedValid),
  822. (nvFilename in FNeedValid))=false then DataObject._Release
  823. else Result:=DataObject;
  824. end;
  825. end;
  826. procedure TDragDropFilesEx.DoMenuPopup(Sender: TObject; AMenu: HMenu;
  827. DataObj: IDataObject; AMinCustCmd:integer; grfKeyState: Longint; pt: TPoint);
  828. var StringList:TStringList;
  829. Reg:TRegistry;
  830. pidlFQ:PItemIDList;
  831. FileName:string;
  832. procedure CreateDragDropHandler(GUID:string);
  833. var Unknown:IUnknown;
  834. ShellExtInit:IShellExtInit;
  835. CMListItem:PCMListItem;
  836. begin
  837. try
  838. Unknown:=CreateComObject(StringToGUID(GUID));
  839. except
  840. Unknown:=nil;
  841. end;
  842. try
  843. if assigned(Unknown) and
  844. (Unknown.QueryInterface(IID_IShellExtInit, ShellExtInit)=S_OK) then
  845. begin
  846. if ShellExtInit.Initialize(pidlFQ, DataObj, 0)=NoError then
  847. begin
  848. New(CMListItem);
  849. if ShellExtInit.QueryInterface(IID_IContextMenu,
  850. CMListItem^.CM)=S_Ok then
  851. begin
  852. CMListItem^.FirstCmd:=AMinCustCmd;
  853. CMListItem^.LastCmd:=AMinCustCmd;
  854. inc(CMListItem^.LastCmd,CMListItem^.CM.QueryContextMenu(
  855. AMenu, 0, CMListItem^.FirstCmd, $7FFF, CMF_NORMAL));
  856. if CMListItem^.LastCmd=CMListItem^.FirstCmd then
  857. Dispose(CMListItem)
  858. else
  859. begin
  860. AMinCustCmd:=CMListItem^.LastCmd;
  861. FCMList.Add(CMListItem);
  862. end;
  863. end;
  864. end;
  865. end;
  866. finally
  867. Unknown:=nil;
  868. ShellExtInit:=nil;
  869. end;
  870. end;
  871. begin
  872. if assigned(FOnSpecifyDropTarget) and FShellExtension.FDragDropHandler then
  873. begin
  874. pidlFQ:=nil;
  875. FOnSpecifyDropTarget(self, true, DragDropControl.ScreenToClient(pt),
  876. pidlFQ, Filename);
  877. if pidlFQ=nil then pidlFQ:=PIDL_GetFromPath(PChar(Filename))
  878. else pidlFQ:=PIDL_Copy(pidlFQ);
  879. StringList:=TStringList.Create;
  880. Reg:=TRegistry.Create;
  881. try
  882. Reg.RootKey:=HKEY_CLASSES_ROOT;
  883. if Reg.OpenKey('Folder\ShellEx\DragDropHandlers', false) then
  884. begin
  885. Reg.GetKeyNames(StringList);
  886. Reg.CloseKey;
  887. while StringList.Count>0 do
  888. begin
  889. { The documentation for the drag-and-drop handlers varies
  890. between many registry-keys, where you find the handlers.
  891. I think, the correct position is "Folder"; "Directory"
  892. should be the key for system-folders! Even, I have found
  893. in the documentation, that you can define drag-and-drop
  894. handlers for the system-folder "printers". Till now, it
  895. doesn't make sense to me. Therefore, I haven't implemented
  896. it!!! }
  897. if Reg.OpenKey('Folder\ShellEx\DragDropHandlers\'+
  898. StringList[StringList.Count-1], false) then
  899. begin
  900. CreateDragDropHandler(Reg.ReadString(''));
  901. Reg.CloseKey;
  902. end;
  903. StringList.Delete(StringList.Count-1);
  904. end;
  905. end;
  906. finally
  907. Stringlist.Free;
  908. Reg.Free;
  909. PIDL_Free(pidlFQ);
  910. end;
  911. end;
  912. inherited DoMenuPopup(Sender, AMenu, DataObj, AMinCustCmd,
  913. grfKeyState, pt);
  914. end;
  915. function TDragDropFilesEx.DoMenuExecCmd(Sender: TObject; AMenu: HMenu;
  916. DataObj:IDataObject; Command:integer; var dwEffect: longint):boolean;
  917. var ICM: TCMInvokeCommandInfo;
  918. i:integer;
  919. CMListItem:PCMListItem;
  920. begin
  921. Result:=false;
  922. try
  923. if FCMList.Count>0 then
  924. for i:=0 to FCMList.Count-1 do
  925. begin
  926. CMListItem:=FCMList.Items[i];
  927. if (CMListItem^.FirstCmd<=Command) and
  928. (CMListItem^.LastCmd>Command) then
  929. begin
  930. FillChar(ICM,SizeOf(TCMInvokeCommandInfo),#0);
  931. ICM.cbSize:=SizeOf(TCMInvokeCommandInfo);
  932. ICM.hwnd:=DragDropControl.Handle;
  933. ICM.lpVerb:=MakeIntResource(Command-CMListItem^.FirstCmd);
  934. ICM.nShow:=SW_SHOWNORMAL;
  935. Result:=CMListItem^.CM.InvokeCommand(ICM)=NOERROR;
  936. break;
  937. end;
  938. end;
  939. finally
  940. if Result=false then
  941. Result:=inherited DoMenuExecCmd(Sender, AMenu, DataObj, Command, dwEffect);
  942. end;
  943. end;
  944. procedure TDragDropFilesEx.DoMenuDestroy(Sender:TObject; AMenu: HMenu);
  945. var CMListItem:PCMListItem;
  946. begin
  947. while FCMList.Count>0 do
  948. begin
  949. CMListItem:=FCMList.Items[FCMList.Count-1];
  950. CMListItem^.CM:=nil;
  951. Dispose(CMListItem);
  952. FCMList.Delete(FCMList.Count-1);
  953. end;
  954. inherited DoMenuDestroy(Sender, AMenu);
  955. end;
  956. function TDragDropFilesEx.TargetHasDropHandler(pidlFQ:PItemIDList; Filename:string;
  957. var dwEffect: longint): boolean;
  958. var ppidlFQ, ppidlRoot, ppidlItem:PItemIDList;
  959. SF:IShellFolder;
  960. DT:IDropTarget;
  961. begin
  962. try
  963. Result:=false;
  964. ppidlFQ:=nil;
  965. ppidlRoot:=nil;
  966. ppidlItem:=nil;
  967. if pidlFQ=nil then ppidlFQ:=PIDL_GetFromPath(PChar(Filename))
  968. else ppidlFQ:=PIDL_Copy(pidlFQ);
  969. PIDL_GetRelative(ppidlFQ, ppidlRoot, ppidlItem);
  970. PIDL_GetFileFolder(ppidlRoot, SF);
  971. if assigned(SF) then
  972. begin
  973. SF.GetUIObjectOf(0,1,ppidlItem,IID_IDropTarget,nil,Pointer(DT));
  974. Result:=Assigned(DT) and FileExists(Filename);
  975. if Assigned(DT) and (dwEffect and not
  976. (DropEffect_Scroll or DropEffect_Link)=DropEffect_None) then
  977. dwEffect:=dwEffect and not DropEffect_Link;
  978. end;
  979. finally
  980. SF:=nil;
  981. DT:=nil;
  982. PIDL_Free(ppidlRoot);
  983. PIDL_Free(ppidlItem);
  984. PIDL_Free(ppidlFQ);
  985. end;
  986. end;
  987. function TDragDropFilesEx.DropHandler(const dataObj: IDataObject; grfKeyState: Longint;
  988. pt: TPoint; var dwEffect: Longint): boolean;
  989. var pidlFQ, ppidlFQ, ppidlRoot, ppidlItem:PItemIDList;
  990. SF:IShellFolder;
  991. DT:IDropTarget;
  992. Filename:string;
  993. pc:array[0..1024] of char;
  994. begin
  995. try
  996. Result:=false;
  997. ppidlRoot:=nil;
  998. ppidlItem:=nil;
  999. ppidlFQ:=nil;
  1000. if (ShellExtensions.FDropHandler=false) or
  1001. (assigned(FOnSpecifyDropTarget)=false) then exit;
  1002. pidlFQ:=nil;
  1003. FOnSpecifyDropTarget(self, false, DragDropControl.ScreenToClient(pt),
  1004. pidlFQ, Filename);
  1005. if pidlFQ=nil then ppidlFQ:=PIDL_GetFromPath(PChar(Filename))
  1006. else ppidlFQ:=PIDL_Copy(pidlFQ);
  1007. PIDL_GetRelative(ppidlFQ, ppidlRoot, ppidlItem);
  1008. PIDL_GetFileFolder(ppidlRoot, SF);
  1009. if assigned(SF) then
  1010. begin
  1011. if (Filename='') and PIDL_GetDisplayName(SF, ppidlItem, SHGDN_FORPARSING,
  1012. pc, sizeof(pc)) then Filename:=StrPas(pc);
  1013. SF.GetUIObjectOf(0,1,ppidlItem,IID_IDropTarget,nil,Pointer(DT));
  1014. if FileExists(Filename) and Assigned(DT) then
  1015. begin
  1016. DT.DragEnter(DataObj, grfKeyState, pt, dwEffect);
  1017. Result:=DT.Drop(DataObj, grfKeyState, pt, dwEffect)=NOERROR;
  1018. end;
  1019. end;
  1020. finally
  1021. SF:=nil;
  1022. DT:=nil;
  1023. PIDL_Free(ppidlRoot);
  1024. PIDL_Free(ppidlItem);
  1025. PIDL_Free(ppidlFQ);
  1026. end;
  1027. end;
  1028. // Register Component ----------------------------------------------------------
  1029. procedure Register;
  1030. begin
  1031. {MP}RegisterComponents({'Shell32'}'DragDrop', [TDragDropFilesEx]);
  1032. end;
  1033. end.