How do I make my DropSouce file acceptable for all purposes that work with files?
I have created a control that represents a list of files and I want to be able to drag and drop files from my control to other applications that work with files. I have implemented the IDragSource interface (as shown below), but when I drag and drop files, the files are only accepted in Windows Explorer, other applications like Firefox, Yahoo Messenger, Photoshop ... do not accept my files. What have I done wrong? I have a feeling that the IDataObject is not configured correctly and I am afraid that I have to implement it myself ... and this is a very difficult task for me because I just started working with interfaces.
Here's the code to reproduce the problem:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ActiveX, ShlObj;
type
TMyControl = class(TMemo, IDropSource)
private
function QueryContinueDrag(fEscapePressed:BOOL; grfKeyState:Longint):HResult; stdcall;
function GiveFeedback(dwEffect:Longint):HResult; stdcall;
procedure DoDragAndDrop;
function GetFileListDataObject:IDataObject;
protected
procedure MouseMove(Shift:TShiftState; X,Y:Integer); override;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
public
MyMemo:TMyControl;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{TMyControl}
function TMyControl.QueryContinueDrag(fEscapePressed:BOOL; grfKeyState:Longint):HResult;
begin
if fEscapePressed then Result:=DRAGDROP_S_CANCEL
else if (grfKeyState and (MK_LBUTTON or MK_RBUTTON) = 0) then Result:=DRAGDROP_S_DROP
else Result:=S_OK;
end;
function TMyControl.GiveFeedback(dwEffect:Longint):HResult;
begin
Result:=DRAGDROP_S_USEDEFAULTCURSORS;
end;
procedure TMyControl.DoDragAndDrop;
var AllowedEffects,DropEffect:Longint;
DataObj:IDataObject;
begin
AllowedEffects:=DROPEFFECT_COPY;
DataObj:=GetFileListDataObject;
if DataObj <> nil then
DoDragDrop(DataObj, self, AllowedEffects, DropEffect);
end;
function TMyControl.GetFileListDataObject:IDataObject;
var Desktop:IShellFolder;
Attr,Eaten:ULONG;
Count,x:Integer;
Pidls:array of PItemIDList;
begin
Result:=nil;
Count:=Lines.Count;
if Count<1 then Exit;
if Failed(SHGetDesktopFolder(Desktop)) then Exit;
SetLength(Pidls,Count);
for x:=0 to Count-1 do Pidls[x]:=nil;
try
for x:=0 to Count-1 do
if Failed(Desktop.ParseDisplayName(0, nil, PWideChar(Lines[x]), Eaten, Pidls[x], Attr)) then Exit;
Desktop.GetUIObjectOf(0, Count, Pidls[0], IDataObject, nil, Result);
finally
for x:=0 to Count-1 do
if Pidls[x]<>nil then CoTaskMemFree(Pidls[x]);
end;
end;
procedure TMyControl.MouseMove(Shift:TShiftState; X,Y:Integer);
begin
if ssLeft in Shift then DoDragAndDrop;
inherited;
end;
//---------------------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
MyMemo:=TMyControl.Create(Form1);
MyMemo.Parent:=Form1;
MyMemo.Align:=alClient;
end;
end.
source to share
The problem is with the wrong Desktop.GetUIObjectOf call. When you call the elements of SomeFolder.GetUIObjectOf, MUST be children of SomeFolder. But in your case, this is not true. Try something like this:
type
PPItemIDList = ^PItemIDList;
function GetFileListDataObject(AParentWnd: HWND; const APath: string; AFileNames: TStrings): IDataObject;
var
Desktop: IShellFolder;
Eaten, Attr: ULONG;
i: Integer;
PathIDList: PItemIDList;
PathShellFolder: IShellFolder;
IDLists: PPItemIDList;
IDListsSize: Integer;
Pos: PPItemIDList;
begin
Result := nil;
if AFileNames.Count < 1 then Exit;
if Failed(SHGetDesktopFolder(Desktop)) then Exit;
try
Attr := 0;
if Failed(Desktop.ParseDisplayName(AParentWnd, nil, PWideChar(APath), Eaten, PathIDList, Attr)) then Exit;
try
if Failed(Desktop.BindToStorage(PathIDList, nil, IShellFolder, PathShellFolder)) then Exit;
try
IDListsSize := SizeOf(PItemIDList) * AFileNames.Count;
GetMem(IDLists, IDListsSize);
try
ZeroMemory(IDLists, IDListsSize);
Pos := IDLists;
for i := 0 to AFileNames.Count - 1 do
begin
Attr := 0;
if Failed(PathShellFolder.ParseDisplayName(0, nil, PWideChar(AFileNames[i]), Eaten, Pos^, Attr)) then Exit;
Inc(Pos);
end;
PathShellFolder.GetUIObjectOf(0, AFileNames.Count, IDLists^, IDataObject, nil, Result);
finally
Pos := IDLists;
for i := 0 to AFileNames.Count - 1 do
begin
if Assigned(Pos^) then
CoTaskMemFree(Pos^);
Inc(Pos);
end;
FreeMem(IDLists);
end;
finally
PathShellFolder := nil;
end;
finally
CoTaskMemFree(PathIDList);
end;
finally
Desktop := nil;
end;
end;
source to share