Subversion Repositories decoder

Rev

Blame | Last modification | View Log | RSS feed

  1. unit DragDropPIDL;
  2.  
  3. // -----------------------------------------------------------------------------
  4. // Project:         Drag and Drop Component Suite
  5. // Module:          DragDropPIDL
  6. // Description:     Implements Dragging & Dropping of PIDLs (files and folders).
  7. // Version:         4.0
  8. // Date:            18-MAY-2001
  9. // Target:          Win32, Delphi 5-6
  10. // Authors:         Anders Melander, anders@melander.dk, http://www.melander.dk
  11. // Copyright        © 1997-2001 Angus Johnson & Anders Melander
  12. // -----------------------------------------------------------------------------
  13.  
  14. interface
  15.  
  16. uses
  17.   DragDrop,
  18.   DropTarget,
  19.   DropSource,
  20.   DragDropFormats,
  21.   DragDropFile,
  22.   Windows,
  23.   ActiveX,
  24.   Classes,
  25.   ShlObj;
  26.  
  27. {$include DragDrop.inc}
  28.  
  29. type
  30. ////////////////////////////////////////////////////////////////////////////////
  31. //
  32. //              TPIDLClipboardFormat
  33. //
  34. ////////////////////////////////////////////////////////////////////////////////
  35. // Supports the 'Shell IDList Array' format.
  36. ////////////////////////////////////////////////////////////////////////////////
  37.   TPIDLClipboardFormat = class(TCustomSimpleClipboardFormat)
  38.   private
  39.     FPIDLs: TStrings; // Used internally to store PIDLs. We use strings to simplify cleanup.
  40.     FFilenames: TStrings;
  41.   protected
  42.     function ReadData(Value: pointer; Size: integer): boolean; override;
  43.     function WriteData(Value: pointer; Size: integer): boolean; override;
  44.     function GetSize: integer; override;
  45.   public
  46.     constructor Create; override;
  47.     destructor Destroy; override;
  48.     function GetClipboardFormat: TClipFormat; override;
  49.     procedure Clear; override;
  50.     function HasData: boolean; override;
  51.     property PIDLs: TStrings read FPIDLs;
  52.     property Filenames: TStrings read FFilenames;
  53.   end;
  54.  
  55.  
  56. type
  57. ////////////////////////////////////////////////////////////////////////////////
  58. //
  59. //              TPIDLDataFormat
  60. //
  61. ////////////////////////////////////////////////////////////////////////////////
  62.   TPIDLDataFormat = class(TCustomDataFormat)
  63.   private
  64.     FPIDLs              : TStrings;
  65.     FFilenames          : TStrings;
  66.   protected
  67.   public
  68.     constructor Create(AOwner: TDragDropComponent); override;
  69.     destructor Destroy; override;
  70.     function Assign(Source: TClipboardFormat): boolean; override;
  71.     function AssignTo(Dest: TClipboardFormat): boolean; override;
  72.     procedure Clear; override;
  73.     function HasData: boolean; override;
  74.     function NeedsData: boolean; override;
  75.     property PIDLs: TStrings read FPIDLs;
  76.     property Filenames: TStrings read FFilenames;
  77.   end;
  78.  
  79.  
  80. type
  81. ////////////////////////////////////////////////////////////////////////////////
  82. //
  83. //              TDropPIDLTarget
  84. //
  85. ////////////////////////////////////////////////////////////////////////////////
  86.   TDropPIDLTarget = class(TCustomDropMultiTarget)
  87.   private
  88.     FPIDLDataFormat     : TPIDLDataFormat;
  89.     FFileMapDataFormat  : TFileMapDataFormat;
  90.     function GetFilenames: TStrings;
  91.   protected
  92.     function GetPIDLs: TStrings;
  93.     function GetPIDLCount: integer;
  94.     function GetMappedNames: TStrings;
  95.     property PIDLs: TStrings read GetPIDLs;
  96.     function DoGetPIDL(Index: integer): pItemIdList;
  97.     function GetPreferredDropEffect: LongInt; override;
  98.   public
  99.     constructor Create(AOwner: TComponent); override;
  100.     destructor Destroy; Override;
  101.  
  102.     // Note: It is the callers responsibility to cleanup
  103.     // the returned PIDLs from the following 3 methods:
  104.     // - GetFolderPidl
  105.     // - GetRelativeFilePidl
  106.     // - GetAbsoluteFilePidl
  107.     // Use the CoTaskMemFree procedure to free the PIDLs.
  108.     function GetFolderPIDL: pItemIdList;
  109.     function GetRelativeFilePIDL(Index: integer): pItemIdList;
  110.     function GetAbsoluteFilePIDL(Index: integer): pItemIdList;
  111.     property PIDLCount: integer read GetPIDLCount; // Includes folder pidl in count
  112.  
  113.     // If you just want the filenames (not PIDLs) then use ...
  114.     property Filenames: TStrings read GetFilenames;
  115.     // MappedNames is only needed if files need to be renamed after a drag or
  116.     // e.g. dragging from 'Recycle Bin'.
  117.     property MappedNames: TStrings read GetMappedNames;
  118.   end;
  119.  
  120. ////////////////////////////////////////////////////////////////////////////////
  121. //
  122. //              TDropPIDLSource
  123. //
  124. ////////////////////////////////////////////////////////////////////////////////
  125.   TDropPIDLSource = class(TCustomDropMultiSource)
  126.   private
  127.     FPIDLDataFormat     : TPIDLDataFormat;
  128.     FFileMapDataFormat  : TFileMapDataFormat;
  129.   protected
  130.     function GetMappedNames: TStrings;
  131.   public
  132.     constructor Create(AOwner: TComponent); override;
  133.     destructor Destroy; override;
  134.     procedure CopyFolderPIDLToList(pidl: PItemIDList);
  135.     procedure CopyFilePIDLToList(pidl: PItemIDList);
  136.     property MappedNames: TStrings read GetMappedNames;
  137.   end;
  138.  
  139.  
  140. ////////////////////////////////////////////////////////////////////////////////
  141. //
  142. //              PIDL utility functions
  143. //
  144. ////////////////////////////////////////////////////////////////////////////////
  145.  
  146. //: GetPIDLsFromData extracts a PIDL list from a memory block and stores the
  147. // PIDLs in a string list.
  148. function GetPIDLsFromData(Data: pointer; Size: integer; PIDLs: TStrings): boolean;
  149.  
  150. //: GetPIDLsFromHGlobal extracts a PIDL list from a global memory block and
  151. // stores the PIDLs in a string list.
  152. function GetPIDLsFromHGlobal(const HGlob: HGlobal; PIDLs: TStrings): boolean;
  153.  
  154. //: GetPIDLsFromFilenames converts a list of files to PIDLs and stores the
  155. // PIDLs in a string list. All the PIDLs are relative to a common root.
  156. function GetPIDLsFromFilenames(const Files: TStrings; PIDLs: TStrings): boolean;
  157.  
  158. //: GetRootFolderPIDL finds the PIDL of the folder which is the parent of a list
  159. // of files. The PIDl is returned as a string. If the files do not share a
  160. // common root, an empty string is returnde.
  161. function GetRootFolderPIDL(const Files: TStrings): string;
  162.  
  163. //: GetFullPIDLFromPath converts a path (filename and path) to a folder/filename
  164. // PIDL pair.
  165. function GetFullPIDLFromPath(Path: string): pItemIDList;
  166.  
  167. //: GetFullPathFromPIDL converts a folder/filename PIDL pair to a full path.
  168. function GetFullPathFromPIDL(PIDL: pItemIDList): string;
  169.  
  170. //: PIDLToString converts a single PIDL to a string.
  171. function PIDLToString(pidl: PItemIDList): string;
  172.  
  173. //: StringToPIDL converts a PIDL string to a PIDL.
  174. function StringToPIDL(const PIDL: string): PItemIDList;
  175.  
  176. //: JoinPIDLStrings merges two PIDL strings into one.
  177. function JoinPIDLStrings(pidl1, pidl2: string): string;
  178.  
  179. //: ConvertFilesToShellIDList converts a list of files to a PIDL list. The
  180. // files are relative to the folder specified by the Path parameter. The PIDLs
  181. // are returned as a global memory handle.
  182. function ConvertFilesToShellIDList(Path: string; Files: TStrings): HGlobal;
  183.  
  184. //: GetSizeOfPIDL calculates the size of a PIDL list.
  185. function GetSizeOfPIDL(PIDL: pItemIDList): integer;
  186.  
  187. //: CopyPIDL makes a copy of a PIDL.
  188. // It is the callers responsibility to free the returned PIDL.              
  189. function CopyPIDL(PIDL: pItemIDList): pItemIDList;
  190.  
  191. {$ifndef BCB}
  192. // Undocumented PIDL utility functions...
  193. // From http://www.geocities.com/SiliconValley/4942/
  194. function ILCombine(pidl1,pidl2:PItemIDList): PItemIDList; stdcall;
  195. function ILFindLastID(pidl: PItemIDList): PItemIDList; stdcall;
  196. function ILClone(pidl: PItemIDList): PItemIDList; stdcall;
  197. function ILRemoveLastID(pidl: PItemIDList): LongBool; stdcall;
  198. function ILIsEqual(pidl1,pidl2: PItemIDList): LongBool; stdcall;
  199. procedure ILFree(Buffer: PItemIDList); stdcall;
  200.  
  201. // Undocumented IMalloc utility functions...
  202. function SHAlloc(BufferSize: ULONG): Pointer; stdcall;
  203. procedure SHFree(Buffer: Pointer); stdcall;
  204. {$endif}
  205.  
  206. ////////////////////////////////////////////////////////////////////////////////
  207. //
  208. //              PIDL/IShellFolder utility functions
  209. //
  210. ////////////////////////////////////////////////////////////////////////////////
  211.  
  212. //: GetShellFolderOfPath retrieves an IShellFolder interface which can be used
  213. // to manage the specified folder.
  214. function GetShellFolderOfPath(FolderPath: string): IShellFolder;
  215.  
  216. //: GetPIDLDisplayName retrieves the display name of the specified PIDL,
  217. // relative to the specified folder.
  218. function GetPIDLDisplayName(Folder: IShellFolder; PIDL: PItemIdList): string;
  219.  
  220. //: GetSubPIDL retrieves the PIDL of the specified file or folder to a PIDL.
  221. // The PIDL is relative to the folder specified by the Folder parameter.
  222. function GetSubPIDL(Folder: IShellFolder; Sub: string): pItemIDList;
  223.  
  224.  
  225. ////////////////////////////////////////////////////////////////////////////////
  226. //
  227. //              Component registration
  228. //
  229. ////////////////////////////////////////////////////////////////////////////////
  230. procedure Register;
  231.  
  232. implementation
  233.  
  234. uses
  235.   ShellAPI,
  236.   SysUtils;
  237.  
  238. resourcestring
  239.   sNoFolderPIDL = 'Folder PIDL must be added first';
  240.  
  241. ////////////////////////////////////////////////////////////////////////////////
  242. //
  243. //              Component registration
  244. //
  245. ////////////////////////////////////////////////////////////////////////////////
  246. procedure Register;
  247. begin
  248.   RegisterComponents(DragDropComponentPalettePage, [TDropPIDLTarget,
  249.     TDropPIDLSource]);
  250. end;
  251.  
  252.  
  253. ////////////////////////////////////////////////////////////////////////////////
  254. //
  255. //              PIDL utility functions
  256. //
  257. ////////////////////////////////////////////////////////////////////////////////
  258. function GetPIDLsFromData(Data: pointer; Size: integer; PIDLs: TStrings): boolean;
  259. var
  260.   i                     : integer;
  261.   pOffset               : ^UINT;
  262.   PIDL                  : PItemIDList;
  263. begin
  264.   PIDLs.Clear;
  265.  
  266.   Result := (Data <> nil) and
  267.     (Size >= integer(PIDA(Data)^.cidl) * (SizeOf(UINT)+SizeOf(PItemIDList)) + SizeOf(UINT));
  268.   if (not Result) then
  269.     exit;
  270.  
  271.   pOffset := @(PIDA(Data)^.aoffset[0]);
  272.   i := PIDA(Data)^.cidl; // Note: Count doesn't include folder PIDL
  273.   while (i >= 0) do
  274.   begin
  275.     PIDL := PItemIDList(UINT(Data)+ pOffset^);
  276.     PIDLs.Add(PIDLToString(PIDL));
  277.     inc(pOffset);
  278.     dec(i);
  279.   end;
  280.   Result := (PIDLs.Count > 1);
  281. end;
  282.  
  283. function GetPIDLsFromHGlobal(const HGlob: HGlobal; PIDLs: TStrings): boolean;
  284. var
  285.   pCIDA                 : PIDA;
  286. begin
  287.   pCIDA := PIDA(GlobalLock(HGlob));
  288.   try
  289.     Result := GetPIDLsFromData(pCIDA, GlobalSize(HGlob), PIDLs);
  290.   finally
  291.     GlobalUnlock(HGlob);
  292.   end;
  293. end;
  294.  
  295. resourcestring
  296.   sBadDesktop = 'Failed to get interface to Desktop';
  297.   sBadFilename = 'Invalid filename: %s';
  298.  
  299. (*
  300. ** Find the folder which is the parent of all the files in a list.
  301. *)
  302. function GetRootFolderPIDL(const Files: TStrings): string;
  303. var
  304.   DeskTopFolder: IShellFolder;
  305.   WidePath: WideString;
  306.   PIDL: pItemIDList;
  307.   PIDLs: TStrings;
  308.   s: string;
  309.   PIDL1, PIDL2: pItemIDList;
  310.   Size, MaxSize: integer;
  311.   i: integer;
  312. begin
  313.   Result := '';
  314.   if (Files.Count = 0) then
  315.     exit;
  316.  
  317.   if (SHGetDesktopFolder(DeskTopFolder) <> NOERROR) then
  318.     raise Exception.Create(sBadDesktop);
  319.  
  320.   PIDLs := TStringList.Create;
  321.   try
  322.     // First convert all paths to PIDLs.
  323.     for i := 0 to Files.Count-1 do
  324.     begin
  325.       WidePath := ExtractFilePath(Files[i]);
  326.       if (DesktopFolder.ParseDisplayName(0, nil, PWideChar(WidePath), PULONG(nil)^,
  327.         PIDL, PULONG(nil)^) <> NOERROR) then
  328.         raise Exception.Create(sBadFilename);
  329.       try
  330.         PIDLs.Add(PIDLToString(PIDL));
  331.       finally
  332.         coTaskMemFree(PIDL);
  333.       end;
  334.     end;
  335.  
  336.     Result := PIDLs[0];
  337.     MaxSize := Length(Result)-SizeOf(Word);
  338.     PIDL := pItemIDList(PChar(Result));
  339.     for i := 1 to PIDLs.Count-1 do
  340.     begin
  341.       s := PIDLs[1];
  342.       PIDL1 := PIDL;
  343.       PIDL2 := pItemIDList(PChar(s));
  344.       Size := 0;
  345.       while (Size < MaxSize) and (PIDL1^.mkid.cb <> 0) and (PIDL1^.mkid.cb = PIDL2^.mkid.cb) and (CompareMem(PIDL1, PIDL2, PIDL1^.mkid.cb)) do
  346.       begin
  347.         inc(Size, PIDL1^.mkid.cb);
  348.         inc(integer(PIDL2), PIDL1^.mkid.cb);
  349.         inc(integer(PIDL1), PIDL1^.mkid.cb);
  350.       end;
  351.       if (Size <> MaxSize) then
  352.       begin
  353.         MaxSize := Size;
  354.         SetLength(Result, Size+SizeOf(Word));
  355.         PIDL1^.mkid.cb := 0;
  356.       end;
  357.       if (Size = 0) then
  358.         break;
  359.     end;
  360.   finally
  361.     PIDLs.Free;
  362.   end;
  363. end;
  364.  
  365. function GetPIDLsFromFilenames(const Files: TStrings; PIDLs: TStrings): boolean;
  366. var
  367.   RootPIDL: string;
  368.   i: integer;
  369.   PIDL: pItemIdList;
  370.   FilePIDL: string;
  371. begin
  372.   Result := False;
  373.   PIDLs.Clear;
  374.   if (Files.Count = 0) then
  375.     exit;
  376.  
  377.   // Get the PIDL of the root folder...
  378.   // All the file PIDLs will be relative to this PIDL
  379.   RootPIDL := GetRootFolderPIDL(Files);
  380.   if (RootPIDL = '') then
  381.     exit;
  382.  
  383.   Result := True;
  384.  
  385.   PIDLS.Add(RootPIDL);
  386.   // Add the file PIDLs (all relative to the root)...
  387.   for i := 0 to Files.Count-1 do
  388.   begin
  389.     PIDL := GetFullPIDLFromPath(Files[i]);
  390.     if (PIDL = nil) then
  391.     begin
  392.       Result := False;
  393.       PIDLs.Clear;
  394.       break;
  395.     end;
  396.     try
  397.       FilePIDL := PIDLToString(PIDL);
  398.     finally
  399.       coTaskMemFree(PIDL);
  400.     end;
  401.     // Remove the root PIDL from the file PIDL making it relative to the root.
  402.     PIDLS.Add(copy(FilePIDL, Length(RootPIDL)-SizeOf(Word)+1,
  403.       Length(FilePIDL)-(Length(RootPIDL)-SizeOf(Word))));
  404.   end;
  405. end;
  406.  
  407. function GetSizeOfPIDL(PIDL: pItemIDList): integer;
  408. var
  409.   Size: integer;
  410. begin
  411.   if (PIDL <> nil) then
  412.   begin
  413.     Result := SizeOf(PIDL^.mkid.cb);
  414.     repeat
  415.       Size := PIDL^.mkid.cb;
  416.       inc(Result, Size);
  417.       inc(integer(PIDL), Size);
  418.     until (Size = 0);
  419.   end else
  420.     Result := 0;
  421. end;
  422.  
  423. function CopyPIDL(PIDL: pItemIDList): pItemIDList;
  424. var
  425.   Size: integer;
  426. begin
  427.   Size := GetSizeOfPIDL(PIDL);
  428.   if (Size > 0) then
  429.   begin
  430.     Result := ShellMalloc.Alloc(Size);
  431.     if (Result <> nil) then
  432.       Move(PIDL^, Result^, Size);
  433.   end else
  434.     Result := nil;
  435. end;
  436.  
  437. function GetFullPIDLFromPath(Path: string): pItemIDList;
  438. var
  439.   DeskTopFolder         : IShellFolder;
  440.   WidePath              : WideString;
  441. begin
  442.   WidePath := Path;
  443.   if (SHGetDesktopFolder(DeskTopFolder) = NOERROR) then
  444.   begin
  445.     if (DesktopFolder.ParseDisplayName(0, nil, PWideChar(WidePath), PULONG(nil)^,
  446.       Result, PULONG(nil)^) <> NOERROR) then
  447.       Result := nil;
  448.   end else
  449.     Result := nil;
  450. end;
  451.  
  452. function GetFullPathFromPIDL(PIDL: pItemIDList): string;
  453. var
  454.   Path: array[0..MAX_PATH] of char;
  455. begin
  456.   if SHGetPathFromIDList(PIDL, Path) then
  457.     Result := Path
  458.   else
  459.     Result := '';
  460. end;
  461.  
  462. // See "Clipboard Formats for Shell Data Transfers" in Ole.hlp...
  463. // (Needed to drag links (shortcuts).)
  464. type
  465.   POffsets = ^TOffsets;
  466.   TOffsets = array[0..$FFFF] of UINT;
  467.  
  468. function ConvertFilesToShellIDList(Path: string; Files: TStrings): HGlobal;
  469. var
  470.   shf: IShellFolder;
  471.   PathPidl, pidl: pItemIDList;
  472.   Ida: PIDA;
  473.   pOffset: POffsets;
  474.   ptrByte: ^Byte;
  475.   i, PathPidlSize, IdaSize, PreviousPidlSize: integer;
  476. begin
  477.   Result := 0;
  478.   shf := GetShellFolderOfPath(path);
  479.   if shf = nil then
  480.     exit;
  481.   // Calculate size of IDA structure ...
  482.   // cidl: UINT ; Directory pidl
  483.   // offset: UINT ; all file pidl offsets
  484.   IdaSize := (Files.Count + 2) * SizeOf(UINT);
  485.  
  486.   PathPidl := GetFullPIDLFromPath(path);
  487.   if PathPidl = nil then
  488.     exit;
  489.   try
  490.     PathPidlSize := GetSizeOfPidl(PathPidl);
  491.  
  492.     //Add to IdaSize space for ALL pidls...
  493.     IdaSize := IdaSize + PathPidlSize;
  494.     for i := 0 to Files.Count-1 do
  495.     begin
  496.       pidl := GetSubPidl(shf, files[i]);
  497.       try
  498.         IdaSize := IdaSize + GetSizeOfPidl(Pidl);
  499.       finally
  500.         ShellMalloc.Free(pidl);
  501.       end;
  502.     end;
  503.  
  504.     //Allocate memory...
  505.     Result := GlobalAlloc(GMEM_SHARE or GMEM_ZEROINIT, IdaSize);
  506.     if (Result = 0) then
  507.       exit;
  508.     try
  509.       Ida := GlobalLock(Result);
  510.       try
  511.         FillChar(Ida^, IdaSize, 0);
  512.  
  513.         //Fill in offset and pidl data...
  514.         Ida^.cidl := Files.Count; //cidl = file count
  515.         pOffset := POffsets(@(Ida^.aoffset));
  516.         pOffset^[0] := (Files.Count+2) * sizeof(UINT); //offset of Path pidl
  517.  
  518.         ptrByte := pointer(Ida);
  519.         inc(ptrByte, pOffset^[0]); //ptrByte now points to Path pidl
  520.         Move(PathPidl^, ptrByte^, PathPidlSize); //copy path pidl
  521.  
  522.         PreviousPidlSize := PathPidlSize;
  523.         for i := 1 to Files.Count do
  524.         begin
  525.           pidl := GetSubPidl(shf,files[i-1]);
  526.           try
  527.             pOffset^[i] := pOffset^[i-1] + UINT(PreviousPidlSize); //offset of pidl
  528.             PreviousPidlSize := GetSizeOfPidl(Pidl);
  529.  
  530.             ptrByte := pointer(Ida);
  531.             inc(ptrByte, pOffset^[i]); //ptrByte now points to current file pidl
  532.             Move(Pidl^, ptrByte^, PreviousPidlSize); //copy file pidl
  533.                                   //PreviousPidlSize = current pidl size here
  534.           finally
  535.             ShellMalloc.Free(pidl);
  536.           end;
  537.         end;
  538.       finally
  539.         GlobalUnLock(Result);
  540.       end;
  541.     except
  542.       GlobalFree(Result);
  543.       raise;
  544.     end;
  545.   finally
  546.     ShellMalloc.Free(PathPidl);
  547.   end;
  548. end;
  549.  
  550. function PIDLToString(pidl: PItemIDList): String;
  551. var
  552.   PidlLength            : integer;
  553. begin
  554.   PidlLength := GetSizeOfPidl(pidl);
  555.   SetLength(Result, PidlLength);
  556.   Move(pidl^, PChar(Result)^, PidlLength);
  557. end;
  558.  
  559. function StringToPIDL(const PIDL: string): PItemIDList;
  560. begin
  561.   Result := ShellMalloc.Alloc(Length(PIDL));
  562.   if (Result <> nil) then
  563.     Move(PChar(PIDL)^, Result^, Length(PIDL));
  564. end;
  565.  
  566. function JoinPIDLStrings(pidl1, pidl2: string): String;
  567. var
  568.   PidlLength            : integer;
  569. begin
  570.   if Length(pidl1) <= 2 then
  571.     PidlLength := 0
  572.   else
  573.     PidlLength := Length(pidl1)-2;
  574.   SetLength(Result, PidlLength + Length(pidl2));
  575.   if PidlLength > 0 then
  576.     Move(PChar(pidl1)^, PChar(Result)^, PidlLength);
  577.   Move(PChar(pidl2)^, Result[PidlLength+1], Length(pidl2));
  578. end;
  579.  
  580. {$ifndef BCB}
  581. // BCB appearantly doesn't support ordinal DLL imports. Strange!
  582. function ILCombine(pidl1,pidl2:PItemIDList): PItemIDList; stdcall;
  583.   external shell32 index 25;
  584. function ILFindLastID(pidl: PItemIDList): PItemIDList; stdcall;
  585.   external shell32 index 16;
  586. function ILClone(pidl: PItemIDList): PItemIDList; stdcall;
  587.   external shell32 index 18;
  588. function ILRemoveLastID(pidl: PItemIDList): LongBool; stdcall;
  589.   external shell32 index 17;
  590. function ILIsEqual(pidl1,pidl2: PItemIDList): LongBool; stdcall;
  591.   external shell32 index 21;
  592. procedure ILFree(Buffer: PItemIDList); stdcall;
  593.   external shell32 index 155;
  594.  
  595. function SHAlloc(BufferSize: ULONG): Pointer; stdcall;
  596.   external shell32 index 196;
  597. procedure SHFree(Buffer: Pointer); stdcall;
  598.   external shell32 index 195;
  599. {$endif}
  600.  
  601. ////////////////////////////////////////////////////////////////////////////////
  602. //
  603. //              PIDL/IShellFolder utility functions
  604. //
  605. ////////////////////////////////////////////////////////////////////////////////
  606. function GetShellFolderOfPath(FolderPath: string): IShellFolder;
  607. var
  608.   DeskTopFolder: IShellFolder;
  609.   PathPidl: pItemIDList;
  610.   WidePath: WideString;
  611.   pdwAttributes: ULONG;
  612. begin
  613.   Result := nil;
  614.   WidePath := FolderPath;
  615.   pdwAttributes := SFGAO_FOLDER;
  616.   if (SHGetDesktopFolder(DeskTopFolder) <> NOERROR) then
  617.     exit;
  618.   if (DesktopFolder.ParseDisplayName(0, nil, PWideChar(WidePath), PULONG(nil)^,
  619.     PathPidl, pdwAttributes) = NOERROR) then
  620.     try
  621.       if (pdwAttributes and SFGAO_FOLDER <> 0) then
  622.         DesktopFolder.BindToObject(PathPidl, nil, IID_IShellFolder,
  623.           // Note: For Delphi 4 and prior, the ppvOut parameter must be a pointer.
  624.           pointer(Result));
  625.     finally
  626.       ShellMalloc.Free(PathPidl);
  627.     end;
  628. end;
  629.  
  630. function GetSubPIDL(Folder: IShellFolder; Sub: string): pItemIDList;
  631. var
  632.   WidePath: WideString;
  633. begin
  634.   WidePath := Sub;
  635.   Folder.ParseDisplayName(0, nil, PWideChar(WidePath), PULONG(nil)^, Result,
  636.     PULONG(nil)^);
  637. end;
  638.  
  639. function GetPIDLDisplayName(Folder: IShellFolder; PIDL: PItemIdList): string;
  640. var
  641.   StrRet: TStrRet;
  642. begin
  643.   Result := '';
  644.   Folder.GetDisplayNameOf(PIDL, 0, StrRet);
  645.   case StrRet.uType of
  646.     STRRET_WSTR: Result := WideCharToString(StrRet.pOleStr);
  647.     STRRET_OFFSET: Result := PChar(UINT(PIDL)+StrRet.uOffset);
  648.     STRRET_CSTR: Result := StrRet.cStr;
  649.   end;
  650. end;
  651.  
  652.  
  653. ////////////////////////////////////////////////////////////////////////////////
  654. //
  655. //              TPIDLsToFilenamesStrings
  656. //
  657. ////////////////////////////////////////////////////////////////////////////////
  658. // Used internally to convert PIDLs to filenames on-demand.
  659. ////////////////////////////////////////////////////////////////////////////////
  660. type
  661.   TPIDLsToFilenamesStrings = class(TStrings)
  662.   private
  663.     FPIDLs: TStrings;
  664.   protected
  665.     function Get(Index: Integer): string; override;
  666.     function GetCount: Integer; override;
  667.     procedure Put(Index: Integer; const S: string); override;
  668.     procedure PutObject(Index: Integer; AObject: TObject); override;
  669.   public
  670.     constructor Create(APIDLs: TStrings);
  671.     procedure Clear; override;
  672.     procedure Delete(Index: Integer); override;
  673.     procedure Insert(Index: Integer; const S: string); override;
  674.     procedure Assign(Source: TPersistent); override;
  675.   end;
  676.  
  677. constructor TPIDLsToFilenamesStrings.Create(APIDLs: TStrings);
  678. begin
  679.   inherited Create;
  680.   FPIDLs := APIDLs;
  681. end;
  682.  
  683. function TPIDLsToFilenamesStrings.Get(Index: Integer): string;
  684. var
  685.   PIDL: string;
  686.   Path: array [0..MAX_PATH] of char;
  687. begin
  688.   if (Index < 0) or (Index > FPIDLs.Count-2) then
  689.     raise Exception.create('Filename index out of range');
  690.   PIDL := JoinPIDLStrings(FPIDLs[0], FPIDLs[Index+1]);
  691.   if SHGetPathFromIDList(PItemIDList(PChar(PIDL)), Path) then
  692.     Result := Path
  693.   else
  694.     Result := '';
  695. end;
  696.  
  697. function TPIDLsToFilenamesStrings.GetCount: Integer;
  698. begin
  699.   if FPIDLs.Count < 2 then
  700.     Result := 0
  701.   else
  702.     Result := FPIDLs.Count-1;
  703. end;
  704.  
  705. procedure TPIDLsToFilenamesStrings.Assign(Source: TPersistent);
  706. begin
  707.   if Source is TStrings then
  708.   begin
  709.     BeginUpdate;
  710.     try
  711.       GetPIDLsFromFilenames(TStrings(Source), FPIDLs);
  712.     finally
  713.       EndUpdate;
  714.     end;
  715.   end else
  716.     inherited Assign(Source);
  717. end;
  718.  
  719. // Inherited abstract methods which do not need implementation...
  720. procedure TPIDLsToFilenamesStrings.Put(Index: Integer; const S: string);
  721. begin
  722. end;
  723.  
  724. procedure TPIDLsToFilenamesStrings.PutObject(Index: Integer; AObject: TObject);
  725. begin
  726. end;
  727.  
  728. procedure TPIDLsToFilenamesStrings.Clear;
  729. begin
  730. end;
  731.  
  732. procedure TPIDLsToFilenamesStrings.Delete(Index: Integer);
  733. begin
  734. end;
  735.  
  736. procedure TPIDLsToFilenamesStrings.Insert(Index: Integer; const S: string);
  737. begin
  738. end;
  739.  
  740. ////////////////////////////////////////////////////////////////////////////////
  741. //
  742. //              TPIDLClipboardFormat
  743. //
  744. ////////////////////////////////////////////////////////////////////////////////
  745. constructor TPIDLClipboardFormat.Create;
  746. begin
  747.   inherited Create;
  748.   FPIDLs := TStringList.Create;
  749.   FFilenames := TPIDLsToFilenamesStrings.Create(FPIDLs);
  750. end;
  751.  
  752. destructor TPIDLClipboardFormat.Destroy;
  753. begin
  754.   FFilenames.Free;
  755.   FPIDLs.Free;
  756.   inherited Destroy;
  757. end;
  758.  
  759. var
  760.   CF_IDLIST: TClipFormat = 0;
  761.  
  762. function TPIDLClipboardFormat.GetClipboardFormat: TClipFormat;
  763. begin
  764.   if (CF_IDLIST = 0) then
  765.     CF_IDLIST := RegisterClipboardFormat(CFSTR_SHELLIDLIST);
  766.   Result := CF_IDLIST;
  767. end;
  768.  
  769. procedure TPIDLClipboardFormat.Clear;
  770. begin
  771.   FPIDLs.Clear;
  772. end;
  773.  
  774. function TPIDLClipboardFormat.HasData: boolean;
  775. begin
  776.   Result := (FPIDLs.Count > 0);
  777. end;
  778.  
  779. function TPIDLClipboardFormat.GetSize: integer;
  780. var
  781.   i                     : integer;
  782. begin
  783.   Result := (FPIDLs.Count+1) * SizeOf(UINT);
  784.   for i := 0 to FPIDLs.Count-1 do
  785.     inc(Result, Length(FPIDLs[i]));
  786. end;
  787.  
  788. function TPIDLClipboardFormat.ReadData(Value: pointer;
  789.   Size: integer): boolean;
  790. begin
  791.   Result := GetPIDLsFromData(Value, Size, FPIDLs);
  792. end;
  793.  
  794. function TPIDLClipboardFormat.WriteData(Value: pointer;
  795.   Size: integer): boolean;
  796. var
  797.   i                     : integer;
  798.   pCIDA                 : PIDA;
  799.   Offset                : integer;
  800.   pOffset               : ^UINT;
  801.   PIDL                  : PItemIDList;
  802. begin
  803.   pCIDA := PIDA(Value);
  804.   pCIDA^.cidl := FPIDLs.Count-1; // Don't count folder PIDL
  805.   pOffset := @(pCIDA^.aoffset[0]); // Points to aoffset[0]
  806.   Offset := (FPIDLs.Count+1)*SizeOf(UINT); // Size of CIDA structure
  807.   PIDL := PItemIDList(integer(pCIDA) + Offset); // PIDLs are stored after CIDA structure.
  808.  
  809.   for i := 0 to FPIDLs.Count-1 do
  810.   begin
  811.     pOffset^ := Offset; // Store relative offset of PIDL into aoffset[i]
  812.     // Copy the PIDL
  813.     Move(PChar(FPIDLs[i])^, PIDL^, length(FPIDLs[i]));
  814.     // Move on to next PIDL
  815.     inc(Offset, length(FPIDLs[i]));
  816.     inc(pOffset);
  817.     inc(integer(PIDL), length(FPIDLs[i]));
  818.   end;
  819.  
  820.   Result := True;
  821. end;
  822.  
  823.  
  824. ////////////////////////////////////////////////////////////////////////////////
  825. //
  826. //              TPIDLDataFormat
  827. //
  828. ////////////////////////////////////////////////////////////////////////////////
  829. constructor TPIDLDataFormat.Create(AOwner: TDragDropComponent);
  830. begin
  831.   inherited Create(AOwner);
  832.   FPIDLs := TStringList.Create;
  833.   TStringList(FPIDLs).OnChanging := DoOnChanging;
  834.   FFilenames := TPIDLsToFilenamesStrings.Create(FPIDLs);
  835. end;
  836.  
  837. destructor TPIDLDataFormat.Destroy;
  838. begin
  839.   FFilenames.Free;
  840.   FPIDLs.Free;
  841.   inherited Destroy;
  842. end;
  843.  
  844. function TPIDLDataFormat.Assign(Source: TClipboardFormat): boolean;
  845. begin
  846.   Result := True;
  847.  
  848.   if (Source is TPIDLClipboardFormat) then
  849.     FPIDLs.Assign(TPIDLClipboardFormat(Source).PIDLs)
  850.  
  851.   else if (Source is TFileClipboardFormat) then
  852.     Result := GetPIDLsFromFilenames(TFileClipboardFormat(Source).Files, FPIDLs)
  853.  
  854.   else
  855.     Result := inherited Assign(Source);
  856. end;
  857.  
  858. function TPIDLDataFormat.AssignTo(Dest: TClipboardFormat): boolean;
  859. begin
  860.   Result := True;
  861.  
  862.   if (Dest is TPIDLClipboardFormat) then
  863.     TPIDLClipboardFormat(Dest).PIDLs.Assign(FPIDLs)
  864.  
  865.   else if (Dest is TFileClipboardFormat) then
  866.     TFileClipboardFormat(Dest).Files.Assign(Filenames)
  867.  
  868.   else
  869.     Result := inherited Assign(Dest);
  870. end;
  871.  
  872. procedure TPIDLDataFormat.Clear;
  873. begin
  874.   FPIDLs.Clear;
  875. end;
  876.  
  877. function TPIDLDataFormat.HasData: boolean;
  878. begin
  879.   Result := (FPIDLs.Count > 0);
  880. end;
  881.  
  882. function TPIDLDataFormat.NeedsData: boolean;
  883. begin
  884.   Result := (FPIDLs.Count = 0);
  885. end;
  886.  
  887.  
  888. ////////////////////////////////////////////////////////////////////////////////
  889. //
  890. //              TDropPIDLTarget
  891. //
  892. ////////////////////////////////////////////////////////////////////////////////
  893. constructor TDropPIDLTarget.Create(AOwner: TComponent);
  894. begin
  895.   inherited Create(AOwner);
  896.   FPIDLDataFormat := TPIDLDataFormat.Create(Self);
  897.   FFileMapDataFormat := TFileMapDataFormat.Create(Self);
  898. end;
  899.  
  900. destructor TDropPIDLTarget.Destroy;
  901. begin
  902.   FPIDLDataFormat.Free;
  903.   FFileMapDataFormat.Free;
  904.   inherited Destroy;
  905. end;
  906.  
  907. function TDropPIDLTarget.GetPIDLs: TStrings;
  908. begin
  909.   Result := FPIDLDataFormat.PIDLs;
  910. end;
  911.  
  912. function TDropPIDLTarget.DoGetPIDL(Index: integer): pItemIdList;
  913. var
  914.   PIDL                  : string;
  915. begin
  916.   PIDL := PIDLs[Index];
  917.   Result := ShellMalloc.Alloc(Length(PIDL));
  918.   if (Result <> nil) then
  919.     Move(PChar(PIDL)^, Result^, Length(PIDL));
  920. end;
  921.  
  922. function TDropPIDLTarget.GetFolderPidl: pItemIdList;
  923. begin
  924.   Result := DoGetPIDL(0);
  925. end;
  926.  
  927. function TDropPIDLTarget.GetRelativeFilePidl(Index: integer): pItemIdList;
  928. begin
  929.   Result := nil;
  930.   if (index < 1) then
  931.     exit;
  932.   Result := DoGetPIDL(Index);
  933. end;
  934.  
  935. function TDropPIDLTarget.GetAbsoluteFilePidl(Index: integer): pItemIdList;
  936. var
  937.   PIDL                  : string;
  938. begin
  939.   Result := nil;
  940.   if (index < 1) then
  941.     exit;
  942.   PIDL := JoinPIDLStrings(PIDLs[0], PIDLs[Index]);
  943.   Result := ShellMalloc.Alloc(Length(PIDL));
  944.   if (Result <> nil) then
  945.     Move(PChar(PIDL)^, Result^, Length(PIDL));
  946. end;
  947.  
  948. function TDropPIDLTarget.GetPIDLCount: integer;
  949. begin
  950.    // Note: Includes folder PIDL in count!
  951.   Result := FPIDLDataFormat.PIDLs.Count;
  952. end;
  953.  
  954. function TDropPIDLTarget.GetFilenames: TStrings;
  955. begin
  956.   Result := FPIDLDataFormat.Filenames;
  957. end;
  958.  
  959. function TDropPIDLTarget.GetMappedNames: TStrings;
  960. begin
  961.   Result := FFileMapDataFormat.FileMaps;
  962. end;
  963.  
  964. function TDropPIDLTarget.GetPreferredDropEffect: LongInt;
  965. begin
  966.   Result := inherited GetPreferredDropEffect;
  967.   if (Result = DROPEFFECT_NONE) then
  968.     Result := DROPEFFECT_COPY;
  969. end;
  970.  
  971. ////////////////////////////////////////////////////////////////////////////////
  972. //
  973. //              TDropPIDLSource
  974. //
  975. ////////////////////////////////////////////////////////////////////////////////
  976. constructor TDropPIDLSource.Create(AOwner: TComponent);
  977. begin
  978.   inherited Create(AOwner);
  979.   FPIDLDataFormat := TPIDLDataFormat.Create(Self);
  980.   FFileMapDataFormat := TFileMapDataFormat.Create(Self);
  981. end;
  982.  
  983. destructor TDropPIDLSource.Destroy;
  984. begin
  985.   FPIDLDataFormat.Free;
  986.   FFileMapDataFormat.Free;
  987.   inherited Destroy;
  988. end;
  989.  
  990. procedure TDropPIDLSource.CopyFolderPIDLToList(pidl: PItemIDList);
  991. begin
  992.   //Note: Once the PIDL has been copied into the list it can be 'freed'.
  993.   FPIDLDataFormat.Clear;
  994.   FFileMapDataFormat.Clear;
  995.   FPIDLDataFormat.PIDLs.Add(PIDLToString(pidl));
  996. end;
  997.  
  998. procedure TDropPIDLSource.CopyFilePIDLToList(pidl: PItemIDList);
  999. begin
  1000.   // Note: Once the PIDL has been copied into the list it can be 'freed'.
  1001.   // Make sure that folder pidl has been added.
  1002.   if (FPIDLDataFormat.PIDLs.Count < 1) then
  1003.     raise Exception.Create(sNoFolderPIDL);
  1004.   FPIDLDataFormat.PIDLs.Add(PIDLToString(pidl));
  1005. end;
  1006.  
  1007. function TDropPIDLSource.GetMappedNames: TStrings;
  1008. begin
  1009.   Result := FFileMapDataFormat.FileMaps;
  1010. end;
  1011.  
  1012.  
  1013. ////////////////////////////////////////////////////////////////////////////////
  1014. //
  1015. //              Initialization/Finalization
  1016. //
  1017. ////////////////////////////////////////////////////////////////////////////////
  1018.  
  1019. initialization
  1020.   // Data format registration
  1021.   TPIDLDataFormat.RegisterDataFormat;
  1022.   // Clipboard format registration
  1023.   TPIDLDataFormat.RegisterCompatibleFormat(TPIDLClipboardFormat, 0, csSourceTarget, [ddRead]);
  1024.   TPIDLDataFormat.RegisterCompatibleFormat(TFileClipboardFormat, 1, csSourceTarget, [ddRead]);
  1025.  
  1026. finalization
  1027.   TPIDLDataFormat.UnregisterDataFormat;
  1028.  
  1029. end.
  1030.