unit uCEFOLEDragAndDrop; {$IFDEF FPC} {$MODE OBJFPC}{$H+} {$ENDIF} {$I cef.inc} {$IFNDEF FPC}{$IFNDEF DELPHI12_UP} // Workaround for "Internal error" in old Delphi versions caused by uint64 handling {$R-} {$ENDIF}{$ENDIF} interface uses {$IFDEF DELPHI16_UP} {$IFDEF MSWINDOWS}WinApi.Windows, WinApi.ShlObj, WinApi.ActiveX,{$ENDIF} System.Classes, System.Math, {$ELSE} {$IFDEF MSWINDOWS}Windows, ShlObj, ActiveX,{$ENDIF} Classes, Math, {$ENDIF} uCEFTypes; const CUSTOM_ARRAY_LENGTH = 25; type TOLEMediumArray = array [0..pred(CUSTOM_ARRAY_LENGTH)] of TStgMedium; TOLEFormatArray = array [0..pred(CUSTOM_ARRAY_LENGTH)] of TFormatEtc; TOLEDragAndDropMgr = class protected function GetStorageForBytes(var aMedium : TStgMedium; const aData : pointer; aLength : NativeUInt) : boolean; function GetStorageForString(var aMedium : TStgMedium; const aData : WideString) : boolean; overload; function GetStorageForString(var aMedium : TStgMedium; const aData : AnsiString) : boolean; overload; function GetStorageForFileDescriptor(var aMedium : TStgMedium; const aFileName : string) : boolean; public function DragEnter(const DataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; virtual; function DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; virtual; function DragLeave: HRESULT; virtual; function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; virtual; end; TOLEEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc) protected FIndex : integer; FNumFormats : integer; FFormatArray : TOLEFormatArray; procedure CopyFromFormatArray(const aSrcFormatArray : TOLEFormatArray); procedure CopyFormatEtc(var aDstFormatEtc : TFormatEtc; const aSrcFormatEtc : TFormatEtc); public constructor Create; overload; constructor Create(const aFormatArray : TOLEFormatArray; aNumFormats : integer; aIndex : integer = 0); overload; destructor Destroy; override; // IEnumFormatEtc {$IFNDEF FPC} function Next(Celt: LongInt; out Rgelt; pCeltFetched: pLongInt): HRESULT; stdcall; function Skip(Celt: Longint): HRESULT; stdcall; {$ELSE} function Next(Celt: ULONG; out Rgelt: FormatEtc; pceltFetched: PULONG = nil): HRESULT; stdcall; function Skip(Celt: ULONG): HRESULT; stdcall; {$ENDIF} function Reset: HRESULT; stdcall; function Clone(out Enum: IEnumFormatEtc): HRESULT; stdcall; end; TOLEDropSource = class(TInterfacedObject, IDropSource) public // IDropSource {$IFNDEF FPC} function QueryContinueDrag(fEscapePressed: bool; grfKeyState: LongInt): HRESULT; stdcall; function GiveFeedback(dwEffect: LongInt): HRESULT; stdcall; {$ELSE} function QueryContinueDrag(fEscapePressed: BOOL; grfKeyState: DWORD): HRESULT; stdcall; function GiveFeedback(dwEffect: DWORD): HRESULT; stdcall; {$ENDIF} end; TOLEDataObject = class(TInterfacedObject, IDataObject) protected FNumFormats : integer; FFormatArray : TOLEFormatArray; FMediumArray : TOLEMediumArray; FAsync : boolean; FInOperation : boolean; function LookupFormatEtc(const aFormatEtc : TFormatEtc) : integer; function DupGlobalMem(hMem : HGLOBAL) : HGLOBAL; public constructor Create(const aFormatArray : TOLEFormatArray; const aMediumArray : TOLEMediumArray; aNumFormats : integer); reintroduce; // IDataObject function GetData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium):HRESULT; stdcall; function GetDataHere(const FormatEtc: TFormatEtc; out Medium: TStgMedium):HRESULT; stdcall; function QueryGetData(const FormatEtc: TFormatEtc): HRESULT; stdcall; function GetCanonicalFormatEtc(const FormatEtc: TFormatEtc; out FormatEtcout: TFormatEtc): HRESULT; stdcall; {$IFNDEF FPC} function SetData(const FormatEtc: TFormatEtc; var Medium: TStgMedium; fRelease: Bool): HRESULT; stdcall; function EnumFormatEtc(dwDirection: LongInt; out aEnumFormatEtc: IEnumFormatEtc): HRESULT; stdcall; function dAdvise(const FormatEtc: TFormatEtc; advf: LongInt; const advsink: IAdviseSink; out dwConnection: LongInt): HRESULT; stdcall; function dUnadvise(dwConnection: LongInt): HRESULT; stdcall; {$ELSE} function SetData(const pformatetc: FORMATETC; {$IFDEF FPC_VER_320}var{$ELSE}const{$ENDIF} medium: STGMEDIUM; FRelease: BOOL): HRESULT; stdcall; function EnumFormatEtc(dwDirection: DWORD; out aEnumFormatEtc: IENUMFORMATETC): HRESULT; stdcall; function DAdvise(const formatetc: FORMATETC; advf: DWORD; const AdvSink: IAdviseSink; out dwConnection: DWORD): HRESULT; stdcall; function DUnadvise(dwconnection: DWORD): HRESULT; stdcall; {$ENDIF} function EnumdAdvise(out EnumAdvise: IEnumStatData): HRESULT; stdcall; end; TOLEDropTarget = class(TInterfacedObject, IDropTarget) protected FManager : TOLEDragAndDropMgr; public constructor Create(const aManager : TOLEDragAndDropMgr); reintroduce; // IDropTarget {$IFNDEF FPC} function DragEnter(const DataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; stdcall; function DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; stdcall; function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; stdcall; {$ELSE} function DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HRESULT; stdcall; function DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HRESULT; stdcall; function Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HRESULT; stdcall; {$ENDIF} function DragLeave: HRESULT; stdcall; end; implementation // ***************************************************** // *************** TOLEDragAndDropMgr ****************** // ***************************************************** function TOLEDragAndDropMgr.GetStorageForBytes(var aMedium : TStgMedium; const aData : pointer; aLength : NativeUInt) : boolean; var TempHandle : HGLOBAL; TempPointer : pointer; begin Result := False; if (aData <> nil) then begin TempHandle := GlobalAlloc(GHND, aLength); if (TempHandle <> 0) then begin TempPointer := GlobalLock(TempHandle); if (TempPointer <> nil) then begin Move(aData^, TempPointer^, aLength); aMedium.hGlobal := TempHandle; aMedium.tymed := TYMED_HGLOBAL; {$IFNDEF FPC} aMedium.unkForRelease := nil; {$ELSE} aMedium.PUnkForRelease := nil; {$ENDIF} GlobalUnlock(TempHandle); Result := True; end else GlobalFree(TempHandle); end; end; end; function TOLEDragAndDropMgr.GetStorageForString(var aMedium : TStgMedium; const aData : WideString) : boolean; var TempPointer : pointer; begin Result := False; if (length(aData) > 0) then begin TempPointer := @aData[1]; Result := GetStorageForBytes(aMedium, TempPointer, length(aData) * SizeOf(WideChar)); end; end; function TOLEDragAndDropMgr.GetStorageForString(var aMedium : TStgMedium; const aData : AnsiString) : boolean; var TempPointer : pointer; begin Result := False; if (length(aData) > 0) then begin TempPointer := @aData[1]; Result := GetStorageForBytes(aMedium, TempPointer, length(aData) * SizeOf(AnsiChar)); end; end; function TOLEDragAndDropMgr.GetStorageForFileDescriptor(var aMedium : TStgMedium; const aFileName : string) : boolean; {$IFDEF FPC} const FD_LINKUI = $8000; {$ENDIF} var TempHandle : HGLOBAL; TempDescriptor : TFileGroupDescriptor; TempPointer : pointer; i, j : integer; TempString : string; begin Result := False; if (length(aFileName) > 0) then begin TempHandle := GlobalAlloc(GHND, sizeof(TFileGroupDescriptor)); if (TempHandle <> 0) then begin TempPointer := GlobalLock(TempHandle); if (TempPointer <> nil) then begin TempDescriptor := TFileGroupDescriptor(TempPointer^); TempDescriptor.cItems := 1; TempDescriptor.fgd[0].dwFlags := FD_LINKUI; TempString := aFileName + #0; i := 1; j := length(TempString); while (i <= j) do begin TempDescriptor.fgd[0].cFileName[pred(i)] := TempString[i]; inc(i); end; aMedium.tymed := TYMED_HGLOBAL; aMedium.hGlobal := TempHandle; {$IFNDEF FPC} aMedium.unkForRelease := nil; {$ELSE} aMedium.PUnkForRelease := nil; {$ENDIF} GlobalUnlock(TempHandle); Result := True; end else GlobalFree(TempHandle); end; end; end; function TOLEDragAndDropMgr.DragEnter(const DataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; begin Result := S_OK; dwEffect := DROPEFFECT_NONE; end; function TOLEDragAndDropMgr.DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; begin Result := S_OK; dwEffect := DROPEFFECT_NONE; end; function TOLEDragAndDropMgr.DragLeave: HRESULT; begin Result := S_OK; end; function TOLEDragAndDropMgr.Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; begin Result := S_OK; dwEffect := DROPEFFECT_NONE; end; // ***************************************************** // **************** TOLEEnumFormatEtc ****************** // ***************************************************** constructor TOLEEnumFormatEtc.Create; begin inherited Create; FIndex := 0; FNumFormats := 0; end; constructor TOLEEnumFormatEtc.Create(const aFormatArray : TOLEFormatArray; aNumFormats, aIndex : integer); begin inherited Create; FIndex := aIndex; FNumFormats := min(aNumFormats, CUSTOM_ARRAY_LENGTH); CopyFromFormatArray(aFormatArray); end; destructor TOLEEnumFormatEtc.Destroy; var i : integer; begin i := 0; while (i < FNumFormats) do begin if (FFormatArray[i].ptd <> nil) then CoTaskMemFree(FFormatArray[i].ptd); inc(i); end; inherited Destroy; end; procedure TOLEEnumFormatEtc.CopyFromFormatArray(const aSrcFormatArray : TOLEFormatArray); var i : integer; begin i := 0; while (i < FNumFormats) do begin CopyFormatEtc(FFormatArray[i], aSrcFormatArray[i]); inc(i); end; end; procedure TOLEEnumFormatEtc.CopyFormatEtc(var aDstFormatEtc : TFormatEtc; const aSrcFormatEtc : TFormatEtc); var Size: Integer; begin aDstFormatEtc.cfFormat := aSrcFormatEtc.cfFormat; aDstFormatEtc.dwAspect := aSrcFormatEtc.dwAspect; aDstFormatEtc.lindex := aSrcFormatEtc.lindex; aDstFormatEtc.tymed := aSrcFormatEtc.tymed; if (aSrcFormatEtc.ptd = nil) then aDstFormatEtc.ptd := nil else begin Size := Max(aSrcFormatEtc.ptd^.tdSize, SizeOf(DVTARGETDEVICE)); aDstFormatEtc.ptd := CoTaskMemAlloc(Size); Move(aSrcFormatEtc.ptd^, aDstFormatEtc.ptd^, Size); end; end; function TOLEEnumFormatEtc.Next {$IFNDEF FPC} (Celt: LongInt; out Rgelt; pCeltFetched: pLongInt): HRESULT; stdcall; {$ELSE} (Celt: ULONG; out Rgelt: FormatEtc; pceltFetched: PULONG): HRESULT; stdcall; {$ENDIF} var i : integer; TempArray : ^TOLEFormatArray; begin i := 0; TempArray := @Rgelt; while (i < Celt) and (FIndex < FNumFormats) do begin CopyFormatEtc(TempArray^[i], FFormatArray[FIndex]); inc(i); inc(FIndex); end; if (pCeltFetched <> nil) then pCeltFetched^ := i; if (i = Celt) then Result := S_OK else Result := S_FALSE; end; function TOLEEnumFormatEtc.Skip {$IFNDEF FPC} (Celt: Longint): HRESULT; stdcall; {$ELSE} (Celt: ULONG): HRESULT; stdcall; {$ENDIF} begin FIndex := FIndex + Celt; if (FIndex <= FNumFormats) then Result := S_OK else Result := S_FALSE; end; function TOLEEnumFormatEtc.Reset: HRESULT; stdcall; begin FIndex := 0; Result := S_OK; end; function TOLEEnumFormatEtc.Clone(out Enum: IEnumFormatEtc): HRESULT; stdcall; begin Enum := TOLEEnumFormatEtc.Create(FFormatArray, FNumFormats, FIndex); Result := S_OK; end; // ***************************************************** // ****************** TOLEDropTarget ******************* // ***************************************************** constructor TOLEDropTarget.Create(const aManager : TOLEDragAndDropMgr); begin inherited Create; FManager := aManager; end; function TOLEDropTarget.DragEnter {$IFNDEF FPC} (const DataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; stdcall; {$ELSE} (const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HRESULT; stdcall; {$ENDIF} begin Result := FManager.DragEnter(DataObj, grfKeyState, pt, Longint(dwEffect)); end; function TOLEDropTarget.DragOver {$IFNDEF FPC} (grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; stdcall; {$ELSE} (grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HRESULT; stdcall; {$ENDIF} begin Result := FManager.DragOver(grfKeyState, pt, Longint(dwEffect)); end; function TOLEDropTarget.DragLeave: HRESULT; stdcall; begin Result := FManager.DragLeave; end; function TOLEDropTarget.Drop {$IFNDEF FPC} (const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HRESULT; stdcall; {$ELSE} (const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HRESULT; stdcall; {$ENDIF} begin Result := FManager.Drop(dataObj, grfKeyState, pt, Longint(dwEffect)); end; // ***************************************************** // ****************** TOLEDropSource ******************* // ***************************************************** function TOLEDropSource.QueryContinueDrag {$IFNDEF FPC} (fEscapePressed: bool; grfKeyState: LongInt): HRESULT; stdcall; {$ELSE} (fEscapePressed: BOOL; grfKeyState: DWORD): HRESULT; stdcall; {$ENDIF} begin if fEscapePressed then Result := DRAGDROP_S_CANCEL else if ((grfKeyState and MK_LBUTTON) = 0) then Result := DRAGDROP_S_DROP else Result := S_OK; end; function TOLEDropSource.GiveFeedback {$IFNDEF FPC} (dwEffect: LongInt): HRESULT; stdcall; {$ELSE} (dwEffect: DWORD): HRESULT; stdcall; {$ENDIF} begin Result := DRAGDROP_S_USEDEFAULTCURSORS; end; // ***************************************************** // ****************** TOLEDataObject ******************* // ***************************************************** constructor TOLEDataObject.Create(const aFormatArray : TOLEFormatArray; const aMediumArray : TOLEMediumArray; aNumFormats : integer); var i : integer; begin inherited Create; FAsync := False; FInOperation := False; FNumFormats := min(aNumFormats, CUSTOM_ARRAY_LENGTH); i := 0; while (i < FNumFormats) do begin FFormatArray[i].cfFormat := aFormatArray[i].cfFormat; FFormatArray[i].ptd := aFormatArray[i].ptd; FFormatArray[i].dwAspect := aFormatArray[i].dwAspect; FFormatArray[i].lindex := aFormatArray[i].lindex; FFormatArray[i].tymed := aFormatArray[i].tymed; FMediumArray[i] := aMediumArray[i]; inc(i); end; end; function TOLEDataObject.LookupFormatEtc(const aFormatEtc : TFormatEtc) : integer; var i : integer; begin Result := -1; i := 0; while (i < FNumFormats) do if ((FFormatArray[i].tymed and aFormatEtc.tymed) <> 0) and (FFormatArray[i].cfFormat = aFormatEtc.cfFormat) and (FFormatArray[i].dwAspect = aFormatEtc.dwAspect) then begin Result := i; exit; end else inc(i); end; function TOLEDataObject.DupGlobalMem(hMem : HGLOBAL) : HGLOBAL; var TempLen : cardinal; TempHandle : HGLOBAL; TempSrc : Pointer; TempDst : Pointer; begin Result := 0; TempSrc := GlobalLock(hMem); if (TempSrc <> nil) then begin TempLen := GlobalSize(hMem); TempHandle := GlobalAlloc(GHND, TempLen); if (TempHandle <> 0) then begin TempDst := GlobalLock(TempHandle); if (TempDst <> nil) then begin Move(TempSrc^, TempDst^, TempLen); Result := TempHandle; GlobalUnlock(TempHandle); end else GlobalFree(TempHandle); end; GlobalUnlock(hMem); end; end; function TOLEDataObject.GetData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium):HRESULT; stdcall; var i : integer; begin i := LookupFormatEtc(FormatEtcIn); if (i < 0) or ((FFormatArray[i].tymed and TYMED_HGLOBAL) = 0) then begin Medium.tymed := TYMED_NULL; {$IFNDEF FPC} Medium.unkForRelease := nil; {$ELSE} Medium.PUnkForRelease := nil; {$ENDIF} Medium.hGlobal := 0; Result := DV_E_FORMATETC; end else begin Medium.tymed := FFormatArray[i].tymed; {$IFNDEF FPC} Medium.unkForRelease := nil; {$ELSE} Medium.PUnkForRelease := nil; {$ENDIF} Medium.hGlobal := DupGlobalMem(FMediumArray[i].hGlobal); Result := S_OK; end; end; function TOLEDataObject.GetDataHere(const FormatEtc: TFormatEtc; out Medium: TStgMedium):HRESULT; stdcall; begin Result := DV_E_FORMATETC; end; function TOLEDataObject.QueryGetData(const FormatEtc: TFormatEtc): HRESULT; stdcall; begin if (LookupFormatEtc(FormatEtc) < 0) then Result := DV_E_FORMATETC else Result := S_OK; end; function TOLEDataObject.GetCanonicalFormatEtc(const FormatEtc: TFormatEtc; out FormatEtcout: TFormatEtc): HRESULT; stdcall; begin FormatEtcout.cfFormat := 0; FormatEtcout.dwAspect := DVASPECT_CONTENT; FormatEtcout.lindex := -1; FormatEtcout.tymed := TYMED_NULL; FormatEtcout.ptd := nil; Result := E_NOTIMPL; end; function TOLEDataObject.SetData {$IFNDEF FPC} (const FormatEtc: TFormatEtc; var Medium: TStgMedium; fRelease: Bool): HRESULT; stdcall; {$ELSE} (const pformatetc: FORMATETC; {$IFDEF FPC_VER_320}var{$ELSE}const{$ENDIF} medium: STGMEDIUM; FRelease: BOOL): HRESULT; stdcall; {$ENDIF} begin Result := E_NOTIMPL; end; function TOLEDataObject.EnumFormatEtc {$IFNDEF FPC} (dwDirection: LongInt; out aEnumFormatEtc: IEnumFormatEtc): HRESULT; stdcall; {$ELSE} (dwDirection: DWORD; out aEnumFormatEtc: IENUMFORMATETC): HRESULT; stdcall; {$ENDIF} begin if (dwDirection = DATADIR_GET) then begin aEnumFormatEtc := TOLEEnumFormatEtc.Create(FFormatArray, FNumFormats); Result := S_OK; end else begin aEnumFormatEtc := nil; if (dwDirection = DATADIR_SET) then Result := E_NOTIMPL else Result := E_INVALIDARG; end; end; function TOLEDataObject.dAdvise {$IFNDEF FPC} (const FormatEtc: TFormatEtc; advf: LongInt; const advsink: IAdviseSink; out dwConnection: LongInt): HRESULT; stdcall; {$ELSE} (const formatetc: FORMATETC; advf: DWORD; const AdvSink: IAdviseSink; out dwConnection: DWORD): HRESULT; stdcall; {$ENDIF} begin Result := OLE_E_ADVISENOTSUPPORTED; end; function TOLEDataObject.dUnadvise {$IFNDEF FPC} (dwConnection: LongInt): HRESULT; stdcall; {$ELSE} (dwconnection: DWORD): HRESULT; stdcall; {$ENDIF} begin Result := OLE_E_ADVISENOTSUPPORTED; end; function TOLEDataObject.EnumdAdvise(out EnumAdvise: IEnumStatData): HRESULT; stdcall; begin Result := OLE_E_ADVISENOTSUPPORTED; end; end.