DragDropFilesEx.pas 36 KB

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