DragDropFilesEx.pas 37 KB

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