| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167 | unit DragDropFilesEx;{  Description  ===========     TDragDropFilesEx is a comfortable and powerful component for COM/OLE     drag&drop operations with files and supports completely the namespace of     Windows (PIDL). The component is a child-class from TDragDrop.  Disclaimer  ==========    The author disclaims all warranties, expressed or implied, including,    without limitation, the warranties of merchantability and of fitness    for any purpose. The author assumes no liability for damages, direct or    consequential, which may result from the use of this component/unit.  Restrictions on Using the Unit / Components  ===========================================      This unit/component is copyright 1998 by Dieter Steinwedel. ALL RIGHTS    ARE RESERVED BY DIETER STEINWEDEL. You are allowed to use it freely    subject to the following restrictions:    • You are not allowed delete or alter the author's name and      copyright in any manner    • You are not allowed to publish a copy, modified version or      compilation neither for payment in any kind nor freely    • You are allowed to create a link to the download in the WWW    • These restrictions and terms apply to you as long as until      I alter them. Changes can found on my homepage}{$ALIGN ON}{$ASSERTIONS OFF}{$BOOLEVAL OFF}{$DENYPACKAGEUNIT OFF}{$EXTENDEDSYNTAX ON}{$HINTS ON}{$IMPORTEDDATA ON}{$LONGSTRINGS ON}{$OPTIMIZATION ON}{$TYPEDADDRESS OFF}{$TYPEINFO OFF}{$WARNINGS ON}interfaceuses  DragDrop, Windows, Classes, SysUtils, ActiveX, PIDL, ShlObj, ComObj, Registry;type  PDropFiles = ^TDropFiles;  TDropFiles = packed record    pFiles: DWORD;                       { offset of file list }    pt: TPoint;                          { drop point (client coords) }    fNC: BOOL;                           { is it on NonClient area }    fWide: BOOL;                         { WIDE character switch }  end;  PItemIDList = ShlObj.PItemIDList;  TFileExMustDnD = (nvFilename, nvPIDL);  TFileExMustDnDSet = set of TFileExMustDnD;  TOnSpecifyDropTarget =    procedure(Sender: TObject; DragDropHandler: Boolean; pt: TPoint; var pidlFQ: PItemIDList; var Filename: string) of object;  PFDDListItem = ^TFDDListItem;  TFDDListItem = record    pidlFQ: PItemIDList;    Name: string;    MappedName: string;  end;  PCMListItem = ^TCMListItem;  TCMListItem = record    FirstCmd: Integer;    LastCmd: Integer;    CM: IContextMenu;  end;  TFileList = class(TList)  private    function Get(Index: Integer): PFDDListItem;    procedure Put(Index: Integer; Item: PFDDListItem);  public    constructor Create;    destructor Destroy; override;    procedure Clear; override;    procedure Delete(Index: Integer);    function Remove(Item: PFDDListItem): Integer;    function First: PFDDListItem;    function Last: PFDDListItem;    function AddItem(ApidlFQ: PItemIDList; AName: string):Integer;    function AddItemEx(ApidlFQ: PItemIDList; AName, AMappedName: string): Integer;    function RenderPIDLs: Boolean;    function RenderNames: Boolean;    property Items[Index: Integer]: PFDDListItem read Get write Put;  end;  TDataObjectFilesEx = class(TDataObject)  private    pidlStream: TMemoryStream;    HDropStream: TMemoryStream;    FilenameMapList: TStringList;    FilenamesAreMapped: Boolean;    FOnRelease: TNotifyEvent;    FPreferCopy: Boolean;  public    constructor Create(AFileList: TFileList; RenderPIDL, RenderFilename, PreferCopy: Boolean);    destructor Destroy; override;    function RenderData(FormatEtc: TFormatEtc; var StgMedium: TStgMedium): HResult; override;    function IsValid(FormatPidl, FormatHDrop: Boolean): Boolean;    property OnRelease: TNotifyEvent read FOnRelease write FOnRelease;  end;  TDropTargetFilesEx = class(TDropTarget)  protected    procedure AcceptDataObject(DataObj: IDataObject; var Accept: Boolean); override;  public    constructor Create(AOwner: TDragDrop);    destructor Destroy; override;    procedure RenderDropped(DataObj: IDataObject; grfKeyState: LongInt; pt: TPoint; var dwEffect: LongInt); override;  end;  TShellExtension = class(TPersistent)  private    FDropHandler: Boolean;    FDragDropHandler: Boolean;  protected    procedure AssignTo(Dest: TPersistent); override;  published    property DropHandler: Boolean read FDropHandler write FDropHandler default False;    property DragDropHandler: Boolean read FDragDropHandler write FDragDropHandler default False;  end;  TDragDropFilesEx = class(TDragDrop)  private    FFileList: TFileList;    FNeedValid: TFileExMustDnDSet;    FCompleteFileList: Boolean;    FFileNamesAreMapped: Boolean;    FOnSpecifyDropTarget: TOnSpecifyDropTarget;    FShellExtension: TShellExtension;    FCMList: TList;    FOnDataObjectRelease: TNotifyEvent;    FPreferCopy: Boolean;  protected    function CreateDataObject:TDataObject; override;    procedure DataObjectRelease(Sender: TObject);    procedure DoMenuPopup(Sender: TObject; AMenu: HMenu; DataObj: IDataObject;       AMinCustCmd: Integer; grfKeyState: LongInt; pt: TPoint); override;    function DoMenuExecCmd(Sender: TObject; AMenu: HMenu; DataObj: IDataObject;      Command: Integer; var dwEffect: LongInt): Boolean; override;    procedure DoMenuDestroy(Sender: TObject; AMenu: HMenu); override;    function DropHandler(const dataObj: IDataObject; grfKeyState: LongInt; pt: TPoint; var dwEffect: LongInt): Boolean; override;  public    constructor Create(AOwner: TComponent); override;    destructor Destroy; override;    function TargetHasDropHandler(pidlFQ: PItemIDList; Filename: string; var dwEffect: LongInt): Boolean;    property FileList: TFileList read FFileList write FFileList;    property FileNamesAreMapped: Boolean read FFileNamesAreMapped;    property PreferCopy: Boolean read FPreferCopy write FPreferCopy;  published    property NeedValid: TFileExMustDnDSet read FNeedValid write FNeedValid;    property CompleteFileList: Boolean read FCompleteFileList write FCompleteFileList default True;    property ShellExtensions: TShellExtension read FShellExtension write FShellExtension;    property OnSpecifyDropTarget: TOnSpecifyDropTarget read FOnSpecifyDropTarget write FOnSpecifyDropTarget;    property OnDropHandlerSucceeded;    property OnDataObjectRelease: TNotifyEvent read FOnDataObjectRelease write FOnDataObjectRelease;  end;procedure Register;implementationuses  Types;const  {$EXTERNALSYM IID_IDropTarget}  IID_IDropTarget: TGUID = (    D1:$00000122;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));type  PByte = ^Byte;// some local functions --------------------------------------------------------procedure CopyHDropToFilelist(var List: TFileList; HDropPtr: PAnsiChar; HDropSize: LongInt);var  s: string;  DropFiles: PDropFiles;  ws: WideString;// List must be empty, before calling ...begin  if (HDropPtr <> nil) and (HDropSize > 0) then  begin    PAnsiChar(DropFiles) := HDropPtr;    Inc(HDropPtr,DropFiles^.pFiles);    if DropFiles^.FWide then    begin      while HDropPtr^ <> #0 do      begin        ws := PWideChar(HDropPtr);        Inc(HDropPtr, (Length(ws) + 1) * 2);        List.AddItem(nil, ws);      end;    end      else    begin      while HDropPtr^ <> #0 do      begin        s := string(HDropPtr);        Inc(HDropPtr, Length(s) + 1);        List.AddItem(nil, s);      end;    end;  end;end;procedure CopyFilenameMapToFilelist(  var List: TFileList; FilenameMapPtr: PChar; FilenameMapSize: LongInt; IsWideChar: Boolean);var  s: string;  idx: LongInt;  ws: WideString;// should be only called after "CopyHDropToFilelist" ...begin  if (FilenameMapPtr<>nil) and (FilenameMapSize>0) then  begin    idx := 0;    if IsWideChar then    begin      while FilenameMapPtr^ <> #0 do      begin        ws := WideCharToString(PWideChar(FilenameMapPtr));        Inc(FilenameMapPtr, (Length(ws) + 1) * 2);        if Idx >= 0 then List.Items[Idx]^.MappedName := ws          else raise Exception.Create('A non-existing filename is mapped');        Inc(Idx);      end;    end      else    begin      while FilenameMapPtr^ <> #0 do      begin        s := StrPas(FilenameMapPtr);        Inc(FilenameMapPtr, Length(s) + 1);        if Idx >= 0 then List.Items[Idx]^.MappedName := s          else raise Exception.Create('A non-existing filename is mapped');        Inc(Idx);      end;    end;  end;end;procedure CopyPIDLsToFilelist(var List: TFileList; pidlPtr: PByte; pidlSize: LongInt);var  size,i,Idx,Count: LongInt;  pidl, pidlRoot: PItemIDList;  LIPtr: ^LongInt;  AddToList: Boolean;begin  if (pidlPtr <> nil) and (pidlSize > 0) then  begin    PByte(LIPtr) := pidlPtr;    count := LIPtr^;    AddToList := (List.Count = 0);    Idx := 0;    Inc(LIPtr);    Inc(pidlPtr, LIPtr^);    i := LIPtr^; // mempos    pidlRoot := nil;    Inc(LIPtr);    while (Count > Idx) and (i < pidlSize) do    begin      PByte(pidl) := pidlPtr;      size := PIDL_GetSize(pidl);      if i = LIPtr^ then      begin // is an item pidl ...        if AddToList then List.AddItem(PIDL_Concatenate(pidlRoot, pidl), '')          // PIDL_Concatenate --> waste of memory          else List.Items[Idx]^.pidlFQ := PIDL_Concatenate(pidlRoot, pidl);        Inc(Idx);        Inc(LIPtr);      end        else pidlRoot := pidl; // is a root pidl ...      Inc(i, size);      Inc(pidlPtr, size);    end;  end;end;// TFileList -------------------------------------------------------------------constructor TFileList.Create;begin  inherited Create;end;destructor TFileList.Destroy;begin  Clear;  inherited Destroy;end;function TFileList.Get(Index: Integer): PFDDListItem;begin  Result := inherited Items[Index];end;procedure TFileList.Put(Index: Integer; Item: PFDDListItem);begin  inherited Items[Index] := Item;end;procedure TFileList.Clear;var  Item: PFDDListItem;  i: Integer;begin  if Count > 0 then  begin    for i:=0 to Count-1 do    begin      Item := inherited Items[i];      if Item <> nil then      begin        PIDL_Free(Item^.pidlFQ);        Dispose(Item);      end;    end;  end;  inherited Clear;end;procedure TFileList.Delete(Index: Integer);var  Item: PFDDListItem;begin  Item := inherited Items[Index];  if Item <> nil then  begin    PIDL_Free(Item^.pidlFQ);    Dispose(Item);  end;  inherited Delete(Index);end;function TFileList.Remove(Item: PFDDListItem): Integer;begin  Result := inherited Remove(Item);  if Item <> nil then  begin    PIDL_Free(Item^.pidlFQ);    Dispose(Item);  end;end;function TFileList.First: PFDDListItem;begin  Result := inherited First;end;function TFileList.Last: PFDDListItem;begin  Result := inherited Last;end;function TFileList.AddItem(ApidlFQ: PItemIDList; AName: string): Integer;var  LI: PFDDListItem;begin  New(LI);  LI^.Name := AName;  LI^.MappedName := '';  LI^.pidlFQ := PIDL_Copy(ApidlFQ);  Result := Add(LI);end;function TFileList.AddItemEx(ApidlFQ:PItemIDList; AName, AMappedName: string): Integer;var  LI: PFDDListItem;begin  New(LI);  LI^.Name := AName;  LI^.MappedName := AMappedName;  LI^.pidlFQ := PIDL_Copy(ApidlFQ);  Result := Add(LI);end;function TFileList.RenderPIDLs:Boolean;var  i: Integer;  piDesktop: IShellFolder;  olePath: WideString;  ulEaten, ulAttribs: ULong;begin  if Failed(SHGetDesktopFolder(piDesktop)) then  begin    Result := False;  end    else  begin    Result := True;    if Count > 0 then    begin      for i:=0 to Count-1 do      begin        if (Items[i] <> nil) and (Items[i]^.pidlFQ = nil) then        begin          if Items[i]^.Name = '' then Result := False            else          begin            olePath := Items[i]^.Name;            ulAttribs := 0;            if Failed(piDesktop.ParseDisplayName(0, nil, POleStr(olePath), ulEaten, Items[i]^.pidlFQ, ulAttribs)) then            begin              Result := False;            end;          end;        end;      end;    end;  end;end;function TFileList.RenderNames: Boolean;var  i: Integer;  SF: IShellFolder;  pc: array[0..1024] of char;  ppidlRoot, ppidlItem: PItemIDList;begin  Result:=True;  if Count>0 then  begin    for i:=0 to Count-1 do    begin      if (Items[i] <> nil) and (Items[i]^.Name = '') then      begin        if Items[i]^.pidlFQ = nil then Result := False          else        begin          PIDL_GetRelative(Items[i]^.pidlFQ, ppidlRoot, ppidlItem);          if PIDL_GetFileFolder(ppidlRoot, SF) then          begin            if PIDL_GetDisplayName(SF, ppidlItem, SHGDN_FORPARSING, pc, SizeOf(pc)) then Items[i]^.Name := StrPas(pc)              else            begin              Items[i]^.Name := '';              Result := False;            end;            PIDL_Free(ppidlRoot);            PIDL_Free(ppidlItem);          end            else Result := False;        end;      end;    end;  end;end;// TDataObjectFilesEx -------------------------------------------------------------constructor TDataObjectFilesEx.Create(AFileList: TFileList; RenderPIDL, RenderFilename, PreferCopy: Boolean);var  i: DWORD;  FE: TFormatEtc;  SM: TStgMedium;  LastpidlRoot, pidlRoot, pidlItem: PItemIDList;  Pos: DWORD;  df: TDropFiles;  pc: array[0..1024] of Char;begin  inherited Create;  pidlStream := TMemoryStream.Create;  HDropStream := TMemoryStream.Create;  FilenameMapList := TStringList.Create;  FilenamesAreMapped := False;  if RenderPIDL then  begin    LastpidlRoot := nil;    pidlStream.SetSize(AFileList.Count * 4 + 8);    pidlStream.Seek(0, 0);    i := AFileList.Count;    pidlStream.Write(i, 4);    i := pidlStream.Size;    pidlStream.Write(i, 4);    pidlStream.Seek(0, 2);    for i := 0 to AFileList.Count - 1 do    begin      if AFileList.Items[i]^.pidlFQ = nil then      begin        pidlStream.SetSize(0);        break;      end;      PIDL_GetRelative(AFileList.Items[i]^.pidlFQ, pidlRoot, pidlItem);      if (LastpidlRoot = nil) or (not PIDL_Equal(LastpidlRoot,pidlRoot)) then      begin        if LastpidlRoot <> nil then PIDL_Free(LastpidlRoot);        LastpidlRoot := PIDL_Copy(pidlRoot);        pidlStream.Write(pidlRoot^, PIDL_GetSize(pidlRoot));      end;      pos := pidlStream.Position;      pidlStream.Write(pidlItem^, PIDL_GetSize(pidlItem));      pidlStream.seek(8 + 4 * i, soBeginning);      pidlStream.Write(pos, 4);      pidlStream.Seek(0, 2);      PIDL_Free(pidlRoot);      PIDL_Free(pidlItem);    end;    PIDL_Free(LastpidlRoot);    if pidlStream.Size <> 0 then    begin      with FE do      begin        cfFormat := CF_SHELLIDLIST;        ptd := nil;        dwAspect := DVASPECT_CONTENT;        lindex := -1;        tymed := TYMED_HGLOBAL;      end;      SetData(FE, SM, False);    end;  end;  if RenderFilename then  begin    with df do    begin      pfiles := SizeOf(TDropFiles);      pt.x := 0;      pt.y := 0;      LongInt(fnc) := 0;      LongInt(Fwide) := 1;    end;    HDropStream.Write(df, SizeOf(df));    for i := 0 to AFileList.Count - 1 do    begin      if AFileList.Items[i]^.Name='' then      begin        HDropStream.SetSize(0);        break;      end;      strPcopy(pc, AFileList.Items[i]^.Name + #0);      HDropStream.Write(pc, (Length(AFileList.Items[i]^.Name) + 1) * SizeOf(pc[0]));      FilenameMapList.Add(AFileList.Items[i]^.MappedName);      if FilenameMapList[i]<>'' then FilenamesAreMapped := True;    end;    if HDropStream.Size <> 0 then    begin      with FE do      begin        cfFormat := CF_HDROP;        ptd := nil;        dwAspect := DVASPECT_CONTENT;        lindex := -1;        tymed := TYMED_HGLOBAL;      end;      SetData(FE, SM, False);      pc[0] := #0;      HDropStream.Write(pc, SizeOf(pc[0]));    end;    if FilenamesAreMapped then    begin      with FE do      begin        cfFormat := CF_FILENAMEMAPW;        ptd := nil;        dwAspect := DVASPECT_CONTENT;        lindex := -1;        tymed := TYMED_HGLOBAL;      end;      SetData(FE, SM, False);    end;  end;  FPreferCopy := PreferCopy;  if PreferCopy then  begin    with FE do    begin      cfFormat := CF_PREFERREDDROPEFFECT;      ptd := nil;      dwAspect := DVASPECT_CONTENT;      lindex := -1;      tymed := TYMED_HGLOBAL;    end;    SetData(FE, SM, False);  end;end;destructor TDataObjectFilesEx.Destroy;begin  if Assigned(OnRelease) then OnRelease(Self);  pidlStream.Free;  HDropStream.Free;  FilenameMapList.Free;  inherited Destroy;end;function TDataObjectFilesEx.RenderData(FormatEtc: TFormatEtc; var StgMedium: TStgMedium): HResult;var  h: HGlobal;  p: Pointer;  FilenameMapStream: TMemoryStream;  i: Integer;  pc: array[0..1024] of Char;begin  Result := E_FAIL;  if FormatEtc.cfFormat = CF_SHELLIDLIST then  begin    h := GlobalAlloc(GHND or GMEM_SHARE, pidlStream.Size);    if h = 0 then    begin      Result := E_OUTOFMEMORY;      Exit;    end;    p := GlobalLock(h);    pidlStream.Seek(0,0);    pidlStream.Read(p^, pidlStream.Size);    GlobalUnlock(h);    with StgMedium do    begin      tymed := TYMED_HGLOBAL;      hGlobal := h;      unkForRelease := nil;    end;    Result := S_OK;  end;  if FormatEtc.cfFormat = CF_HDROP then  begin    h := GlobalAlloc(GHND or GMEM_SHARE, HDropStream.Size);    if h = 0 then    begin      Result := E_OUTOFMEMORY;      Exit;    end;    p := GlobalLock(h);    HDropStream.Seek(0,0);    HDropStream.Read(p^, HDropStream.Size);    GlobalUnlock(h);    with StgMedium do    begin      tymed :=TYMED_HGLOBAL;      hGlobal := h;      unkForRelease := nil;    end;    Result := S_OK;  end;  if (FormatEtc.cfFormat = CF_FILENAMEMAP) or (FormatEtc.cfFormat = CF_FILENAMEMAPW) then  begin    FilenameMapStream := TMemoryStream.Create;    if (FormatEtc.cfFormat=CF_FILENAMEMAPW) then    begin      for i := 0 to FilenameMapList.Count - 1 do      begin        StringToWideChar(FilenameMapList[i], PWideChar(@pc), SizeOf(pc));        FilenameMapStream.Write(pc, Length(WideString(PWideChar(@pc))) * 2 + 2);      end;      pc[0]:=#0;      pc[1]:=#0;      FilenameMapStream.Write(pc, 2);    end      else    begin      for i := 0 to FilenameMapList.count-1 do      begin        strPcopy(pc,FilenameMapList[i] + #0);        FilenameMapStream.Write(pc, Length(FilenameMapList[i]) + 1);      end;      pc[0] := #0;      FilenameMapStream.Write(pc, 1);    end;    h := GlobalAlloc(GHND or GMEM_SHARE, FilenameMapStream.Size);    if h = 0 then    begin      Result := E_OUTOFMEMORY;      FilenameMapStream.Free;      Exit;    end;    p := GlobalLock(h);    FilenameMapStream.Seek(0,0);    FilenameMapStream.Read(p^, FilenameMapStream.Size);    FilenameMapStream.Free;    GlobalUnlock(h);    with StgMedium do    begin      tymed := TYMED_HGLOBAL;      hGlobal := h;      unkForRelease := nil;    end;    Result := S_OK;  end;  if (FormatEtc.cfFormat = CF_PREFERREDDROPEFFECT) and FPreferCopy then  begin    h := GlobalAlloc(GHND or GMEM_SHARE, SizeOf(DWORD));    if h = 0 then    begin      Result := E_OUTOFMEMORY;      Exit;    end;    p := GlobalLock(h);    PDWORD(p)^ := DROPEFFECT_COPY;    GlobalUnlock(h);    with StgMedium do    begin      tymed := TYMED_HGLOBAL;      hGlobal := h;      unkForRelease := nil;    end;    Result := S_OK;  end;end;function TDataObjectFilesEx.IsValid(FormatPidl, FormatHDrop: Boolean): Boolean;begin  Result:= not ((FormatPidl and (pidlStream.Size = 0)) or (FormatHDrop and (HDropStream.Size = 0)));end;// TDropTargetFilesEx -------------------------------------------------------------constructor TDropTargetFilesEx.Create(AOwner: TDragDrop);begin  inherited Create(AOwner);end;destructor TDropTargetFilesEx.Destroy;begin  inherited Destroy;end;procedure TDropTargetFilesEx.AccepTDataObject(DataObj: IDataObject; var Accept: Boolean);var  FE: TFormatEtc;  HasHDrop, HasIDList: Boolean;begin  Accept := False;  with FE do  begin    cfFormat := CF_HDROP;    ptd := nil;    dwAspect := DVASPECT_CONTENT;    lindex := -1;    tymed := TYMED_HGLOBAL;  end;  HasHDrop := (DataObj.QueryGetData(FE) = S_OK);  if HasHDrop or (not (nvFilename in TDragDropFilesEx(FOwner).FNeedValid)) then  begin    with FE do    begin      cfFormat := CF_SHELLIDLIST;      ptd := nil;      dwAspect := DVASPECT_CONTENT;      lindex := -1;      tymed := TYMED_HGLOBAL;    end;    HasIDList := (DataObj.QueryGetData(FE) = S_OK);    if HasIDList or (not (nvPIDL in TDragDropFilesEx(FOwner).FNeedValid)) then    begin      Accept := HasIDList or HasHDrop;    end;  end;end;procedure TDropTargetFilesEx.RenderDropped(  DataObj: IDataObject; grfKeyState: LongInt; pt: TPoint; var dwEffect: LongInt);var  FormatEtc: TFormatEtc;  StgMedium: TStgMedium;  HDropSize, pidlSize, FileNameMapSize: LongInt;  HDropPtr, pidlPtr, FileNameMapPtr: Pointer;  HDropHandle,pidlHandle, FileNameMapHandle: THandle;  IsWideChar: Boolean;begin  TDragDropFilesEx(FOwner).FFileList.Clear;  // get "CF_HDROP" items  with FormatEtc do  begin    cfFormat := CF_HDROP;    ptd := nil;    dwAspect := DVASPECT_CONTENT;    lindex := -1;    tymed := TYMED_HGLOBAL;  end;  if DataObj.GetData(FormatEtc, StgMedium) = S_OK then HDropHandle := StgMedium.HGlobal    else HDropHandle := 0;  if HDropHandle <> 0 then  begin    try      HDropSize := GlobalSize(HDropHandle);      HDropPtr := GlobalLock(HDropHandle);      CopyHDropToFilelist(TDragDropFilesEx(FOwner).FFileList, HDropPtr, HDropSize);    finally      GlobalUnLock(HDropHandle);      ReleaseStgMedium(StgMedium);    end;    // CF_FILENAMEMAP makes only sense if CF_HDROP exists ...    // get "CF_FILENAMEMAP" or "CF_FILENAMEMAPW" items    with FormatEtc do    begin      cfFormat := CF_FILENAMEMAP;      ptd := nil;      dwAspect := DVASPECT_CONTENT;      lindex := -1;      tymed := TYMED_HGLOBAL;    end;    IsWideChar := False;    FileNameMapHandle := 0;    if DataObj.GetData(FormatEtc, StgMedium) = S_OK then FileNameMapHandle := StgMedium.HGlobal      else    begin      with FormatEtc do      begin        cfFormat := CF_FILENAMEMAPW;        ptd := nil;        dwAspect := DVASPECT_CONTENT;        lindex := -1;        tymed := TYMED_HGLOBAL;      end;      if DataObj.GetData(FormatEtc, StgMedium) = S_OK then      begin        FileNameMapHandle := StgMedium.HGlobal;        IsWideChar := True;      end;    end;    if FileNameMapHandle <> 0 then    begin      TDragDropFilesEx(FOwner).FFileNamesAreMapped := True;      try        FileNameMapSize := GlobalSize(FileNameMapHandle);        FileNameMapPtr := GlobalLock(FileNameMapHandle);        CopyFileNameMapToFilelist(TDragDropFilesEx(FOwner).FFileList, FileNameMapPtr, FileNameMapSize, IsWideChar);     finally        GlobalUnLock(FileNameMapHandle);        ReleaseStgMedium(StgMedium);      end;    end      else TDragDropFilesEx(FOwner).FFileNamesAreMapped := False;  end    else TDragDropFilesEx(FOwner).FFileNamesAreMapped := False;  // get "CF_SHELLIDLIST" items  with FormatEtc do  begin    cfFormat := CF_SHELLIDLIST;    ptd := nil;    dwAspect := DVASPECT_CONTENT;    lindex := -1;    tymed := TYMED_HGLOBAL;  end;  if DataObj.GetData(FormatEtc, StgMedium) = S_OK then pidlHandle := StgMedium.HGlobal    else pidlHandle := 0;  if pidlHandle <> 0 then  begin    try      pidlSize := GlobalSize(pidlHandle);      PidlPtr := GlobalLock(pidlHandle);      CopyPIDLsToFilelist(TDragDropFilesEx(FOwner).FFileList, pidlPtr, pidlSize);    finally      GlobalUnLock(pidlHandle);      ReleaseStgMedium(StgMedium);    end;  end;end;// TShellExtension ---------------------------------------------------procedure TShellExtension.AssignTo(Dest: TPersistent);begin  if Dest is TShellExtension then  begin    with TShellExtension(Dest) do    begin      FDropHandler := Self.FDropHandler;      FDragDropHandler := Self.FDragDropHandler;    end;  end    else inherited AssignTo(Dest);end;// TDragDropFilesEx ---------------------------------------------------------------constructor TDragDropFilesEx.Create(AOwner: TComponent);begin  inherited Create(AOwner);  FFileList := TFileList.Create;  FDropTarget._Release;  FDropTarget := TDropTargetFilesEx.Create(self);  FCompleteFileList := True;  SourceCompatibility := [];  FFileNamesAreMapped := False;  FCMList := TList.Create;  FShellExtension := TShellExtension.Create;  FPreferCopy := False;end;destructor TDragDropFilesEx.Destroy;begin  FCMList.Free;  FFileList.Free;  FShellExtension.Free;  inherited;end;procedure TDragDropFilesEx.DataObjectRelease(Sender: TObject);begin  if Assigned(OnDataObjectRelease) then OnDataObjectRelease(Self);end;function TDragDropFilesEx.CreateDataObject: TDataObject;var  DataObject: TDataObjectFilesEx;  RFName, RPidl: Boolean;begin  Result := nil;  if FCompleteFileList then  begin    RFName := FFileList.RenderNames;    RPidl := FFileList.RenderPIDLs;    if ((nvFilename in FNeedValid) and (not RFName)) or       ((nvPIDL in FNeedValid) and (not RPidl)) then    begin      exit;    end;  end    else  begin    RFName := True;    RPidl := True;  end;  if FFileList.Count > 0 then  begin    DataObject := TDataObjectFilesEx.Create(FFileList, RPidl, RFname, FPreferCopy);    DataObject.OnRelease := DataObjectRelease;    if not DataObject.IsValid((nvPIDL in FNeedValid), (nvFilename in FNeedValid)) then DataObject._Release      else Result := DataObject;  end;end;procedure TDragDropFilesEx.DoMenuPopup(  Sender: TObject; AMenu: HMenu; DataObj: IDataObject; AMinCustCmd:Integer; grfKeyState: LongInt; pt: TPoint);var  StringList: TStringList;  Reg: TRegistry;  pidlFQ: PItemIDList;  FileName: string;  procedure CreateDragDropHandler(GUID:string);  var    Unknown: IUnknown;    ShellExtInit: IShellExtInit;    CMListItem: PCMListItem;  begin    try      Unknown := CreateComObject(StringToGUID(GUID));    except      Unknown := nil;    end;    try      if Assigned(Unknown) and         (Unknown.QueryInterface(IID_IShellExtInit, ShellExtInit) = S_OK) then      begin        if ShellExtInit.Initialize(pidlFQ, DataObj, 0) = NoError then        begin          New(CMListItem);          if ShellExtInit.QueryInterface(IID_IContextMenu, CMListItem^.CM) = S_OK then          begin            CMListItem^.FirstCmd := AMinCustCmd;            CMListItem^.LastCmd := AMinCustCmd;            Inc(CMListItem^.LastCmd, CMListItem^.CM.QueryContextMenu(AMenu, 0, CMListItem^.FirstCmd, $7FFF, CMF_NORMAL));            if CMListItem^.LastCmd = CMListItem^.FirstCmd then Dispose(CMListItem)              else            begin              AMinCustCmd := CMListItem^.LastCmd;              FCMList.Add(CMListItem);            end;          end;        end;      end;    finally      Unknown := nil;      ShellExtInit := nil;    end;  end;begin  if Assigned(FOnSpecifyDropTarget) and FShellExtension.FDragDropHandler then  begin    pidlFQ := nil;    FOnSpecifyDropTarget(self, True, DragDropControl.ScreenToClient(pt),       pidlFQ, Filename);    if pidlFQ=nil then pidlFQ:=PIDL_GetFromPath(PChar(Filename))    else pidlFQ:=PIDL_Copy(pidlFQ);    StringList:=TStringList.Create;    Reg:=TRegistry.Create;    try      Reg.RootKey := HKEY_CLASSES_ROOT;      if Reg.OpenKey('Folder\ShellEx\DragDropHandlers', False) then      begin        Reg.GetKeyNames(StringList);        Reg.CloseKey;        while StringList.Count>0 do        begin          { The documentation for the drag-and-drop handlers varies            between many registry-keys, where you find the handlers.            I think, the correct position is "Folder"; "Directory"            should be the key for system-folders! Even, I have found            in the documentation, that you can define drag-and-drop            handlers for the system-folder "printers". Till now, it            doesn't make sense to me. Therefore, I haven't implemented            it }          if Reg.OpenKey('Folder\ShellEx\DragDropHandlers\' + StringList[StringList.Count - 1], False) then          begin            CreateDragDropHandler(Reg.ReadString(''));            Reg.CloseKey;          end;          StringList.Delete(StringList.Count - 1);        end;      end;    finally      Stringlist.Free;      Reg.Free;      PIDL_Free(pidlFQ);    end;  end;  inherited DoMenuPopup(Sender, AMenu, DataObj, AMinCustCmd, grfKeyState, pt);end;function TDragDropFilesEx.DoMenuExecCmd(  Sender: TObject; AMenu: HMenu; DataObj: IDataObject; Command: Integer; var dwEffect: LongInt): Boolean;var  ICM: TCMInvokeCommandInfo;  i: Integer;  CMListItem: PCMListItem;begin  Result := False;  try    if FCMList.Count>0 then    begin      for i := 0 to FCMList.Count-1 do      begin        CMListItem := FCMList.Items[i];        if (CMListItem^.FirstCmd <= Command) and           (CMListItem^.LastCmd > Command) then        begin          FillChar(ICM, SizeOf(TCMInvokeCommandInfo), #0);          ICM.cbSize := SizeOf(TCMInvokeCommandInfo);          ICM.hwnd := DragDropControl.Handle;          ICM.lpVerb := MakeIntResourceA(Command-CMListItem^.FirstCmd);          ICM.nShow := SW_SHOWNORMAL;          Result := (CMListItem^.CM.InvokeCommand(ICM) = NOERROR);          break;        end;      end;    end;  finally    if not Result then    begin      Result := inherited DoMenuExecCmd(Sender, AMenu, DataObj, Command, dwEffect);    end;  end;end;procedure TDragDropFilesEx.DoMenuDestroy(Sender: TObject; AMenu: HMenu);var  CMListItem: PCMListItem;begin  while FCMList.Count > 0 do  begin    CMListItem := FCMList.Items[FCMList.Count - 1];    CMListItem^.CM := nil;    Dispose(CMListItem);    FCMList.Delete(FCMList.Count - 1);  end;  inherited DoMenuDestroy(Sender, AMenu);end;function TDragDropFilesEx.TargetHasDropHandler(  pidlFQ: PItemIDList; Filename: string; var dwEffect: LongInt): Boolean;var  ppidlFQ, ppidlRoot, ppidlItem: PItemIDList;  SF: IShellFolder;  DT: IDropTarget;begin  try    Result := False;    ppidlFQ := nil;    ppidlRoot := nil;    ppidlItem := nil;    if pidlFQ = nil then ppidlFQ := PIDL_GetFromPath(PChar(Filename))      else ppidlFQ := PIDL_Copy(pidlFQ);    PIDL_GetRelative(ppidlFQ, ppidlRoot, ppidlItem);    PIDL_GetFileFolder(ppidlRoot, SF);    if Assigned(SF) then    begin      SF.GetUIObjectOf(0,1,ppidlItem,IID_IDropTarget,nil,Pointer(DT));      Result := Assigned(DT) and FileExists(Filename);      if Assigned(DT) and (dwEffect and not (DROPEFFECT_SCROLL or DROPEFFECT_LINK) = DROPEFFECT_NONE) then      begin        dwEffect := dwEffect and not DROPEFFECT_LINK;      end;    end;  finally    SF := nil;    DT := nil;    PIDL_Free(ppidlRoot);    PIDL_Free(ppidlItem);    PIDL_Free(ppidlFQ);  end;end;function TDragDropFilesEx.DropHandler(  const dataObj: IDataObject; grfKeyState: LongInt; pt: TPoint; var dwEffect: LongInt): Boolean;var  pidlFQ, ppidlFQ, ppidlRoot, ppidlItem: PItemIDList;  SF: IShellFolder;  DT: IDropTarget;  Filename: string;  pc: array[0..1024] of Char;begin  try    Result := False;    ppidlRoot := nil;    ppidlItem := nil;    ppidlFQ := nil;    if ShellExtensions.FDropHandler and Assigned(FOnSpecifyDropTarget) then    begin      pidlFQ := nil;      FOnSpecifyDropTarget(Self, False, DragDropControl.ScreenToClient(pt), pidlFQ, Filename);      if pidlFQ = nil then ppidlFQ := PIDL_GetFromPath(PChar(Filename))        else ppidlFQ := PIDL_Copy(pidlFQ);      PIDL_GetRelative(ppidlFQ, ppidlRoot, ppidlItem);      PIDL_GetFileFolder(ppidlRoot, SF);      if Assigned(SF) then      begin        if (Filename = '') and           PIDL_GetDisplayName(SF, ppidlItem, SHGDN_FORPARSING, pc, SizeOf(pc)) then        begin          Filename := StrPas(pc);        end;        SF.GetUIObjectOf(0, 1, ppidlItem, IID_IDropTarget, nil, Pointer(DT));        if FileExists(Filename) and Assigned(DT) then        begin          DT.DragEnter(DataObj, grfKeyState, pt, dwEffect);          Result := (DT.Drop(DataObj, grfKeyState, pt, dwEffect) = NOERROR);        end;      end;    end;  finally    SF := nil;    DT := nil;    PIDL_Free(ppidlRoot);    PIDL_Free(ppidlItem);    PIDL_Free(ppidlFQ);  end;end;// Register Component ----------------------------------------------------------procedure Register;begin  RegisterComponents('DragDrop', [TDragDropFilesEx]);end;end.
 |