| 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}
- interface
- uses
- 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;
- implementation
- uses
- 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.
|