DragDropFilesEx.pas 35 KB

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