









Actually, this article was my post at Delphindo mailing list to answer question how to get list of filenames dragged and dropped from Windows Explorer onto a control.
If you ever use WinAmp, you know that we can add audio file into playlist by dragging and dropping files from Windows Explorer into WinAmp playlist listbox. The question is how?
To get notified when drag-drop happen, we must register our window handle by calling DragAcceptFiles() function declared in ShellAPI.pas unit.
DragAcceptFiles(aWinHandle,FAccept);
This function expects two parameters. First parameter is handle that will receive drag-drop operation, second parameter tells whether it is registration or unregistration process. If it is true, window handle will receive notification when drag drop operation occured and if it is false, window handle will be removed from drag- drop notification list.
WM_DROPFILES message will be sent to window handle registered for notification. wParam will hold handle to drag drop operation occured. We use this handle to get more information such retrieve number of files drag-dropped and also their names.
Because we use window handle, only control that have window handle is able to receive this message. WM_DROPFILES will be sent via window procedure of the corresponding window handle.
DragQueryFile() is used to get number of file drag-dropped.
total_file:=DragQueryFile(hDrop,$FFFFFFFF,nil,0);
This function needs four parameters, but to get number of file, only two parameters that is relevant. The rest is set to nil and 0. First parameter is drag drop handle, and second is file index, whioch must be set to $FFFFFFFF.
We need to know length of filename, to be able to allocate buffer to hold filename data. To get length of filename we use same function, DragQueyFile(), but with a little bit different parameters, i.e, we set Fileindex to 0 - (total_file-1).
filename_len:=DragQueryFile(hDrop,FileIndex,nil,0);
To get file name, we use DragQueyFile(), Fileindex is set to 0 - (total_file-1),
DragQueryFile(hDrop,FileIndex,aFilename,filename_len);
afilename is data of type of PChar previously allocated with buffer length of filename_len.
We callDragFinish() to tell Windows that we are done with drag drop operation. This function will free memories allocated for filename buffer. We pass drag drop handle to this function.
Next, we create its implementation. We encapsulate drag drop functionalities into one component that will make drag drop operation to a control easier. Its source code is as following code snippet:
{
================================
TDragDrop v.1.0
=================================
Komponen yg mengenkapsulasi proses
drag drop files dari file manager
ke window control
=================================
(c)2004 Zamrony P Juhara
=================================
http://www.juhara.com
=================================
}
unit DragDrop;
interface
uses
SysUtils, Classes,Controls,Windows,Messages;
type
TDragDropFilesEvent=procedure(Sender:TObject;const FileIndex,TotalFile:integer;
const Filename:string) of object;
TDragDrop = class(TComponent)
private
OldWndProc:TWndMethod;
FAccept: boolean;
FOnDragDropFiles: TDragDropFilesEvent;
FControl: TWinControl;
procedure SetDragAccept;
procedure WndProc(var msg:TMessage);
procedure AcceptFiles(const hDrop:THandle);
function GetTotalDroppedFiles(const hDrop:THandle):integer;
function GetDroppedFilename(const hDrop:THandle;const FileIndex:integer):string;
function GetDroppedFilenameLength(const hDrop:THandle;const FileIndex:integer):cardinal;
procedure AcceptFinish(const hDrop:THandle);
procedure SetAccept(const Value: boolean);
procedure SetControl(const Value: TWinControl);
procedure SetOnDragDropFiles(const Value: TDragDropFilesEvent);
protected
procedure DoDragDropFiles(const FileIndex,TotalFile:integer;
const Filename:string);virtual;
public
published
property Control:TWinControl read FControl write SetControl;
property Accept:boolean read FAccept write SetAccept;
property OnDragDropFiles:TDragDropFilesEvent read FOnDragDropFiles write SetOnDragDropFiles;
end;
procedure Register;
implementation
uses ShellAPI;
procedure Register;
begin
RegisterComponents('juhara Components', [TDragDrop]);
end;
{ TDragDrop }
procedure TDragDrop.AcceptFiles(const hDrop:THandle);
var i,totalFile:integer;
Filename:string;
begin
TotalFile:=GetTotalDroppedFiles(hDrop);
for i:=0 to TotalFile-1 do
begin
FileName:=GetDroppedFilename(hDrop,i);
DoDragDropFiles(i,TotalFile,Filename);
if Assigned(FOnDragDropFiles) then
FOnDragDropFiles(Self,i,TotalFile,Filename);
end;
AcceptFinish(hDrop);
end;
procedure TDragDrop.AcceptFinish(const hDrop: THandle);
begin
DragFinish(hDrop);
end;
procedure TDragDrop.DoDragDropFiles(const FileIndex, TotalFile: integer;
const Filename: string);
begin
//defaultnya gak melakukan apa-apa
//override utk mengubah proses AcceptFiles
end;
function TDragDrop.GetDroppedFilename(const hDrop: THandle;
const FileIndex: integer): string;
var afilename:PChar;
size:cardinal;
begin
result:='';
size:=GetDroppedFilenameLength(hDrop,FileIndex);
if Size<>0 then
begin
aFilename:=StrAlloc(Size);
try
DragQueryFile(hDrop,FileIndex,aFilename,Size);
result:=aFilename;
finally
StrDispose(aFilename);
end;
end;
end;
function TDragDrop.GetDroppedFilenameLength(const hDrop: THandle;
const FileIndex: integer): cardinal;
var size:cardinal;
begin
size:=0;
size:=DragQueryFile(hDrop,FileIndex,nil,size);
//tambah satu karena kita menggunakan string
result:=size+1;
end;
function TDragDrop.GetTotalDroppedFiles(const hDrop: THandle): integer;
begin
result:=DragQueryFile(hDrop,$FFFFFFFF,nil,0);
end;
procedure TDragDrop.SetAccept(const Value: boolean);
begin
FAccept := Value;
//kalo runtime
if not(csDesigning in ComponentState) then
SetDragAccept;
end;
procedure TDragDrop.SetControl(const Value: TWinControl);
begin
FControl := Value;
//if runtime
if not(csDesigning in ComponentState) then
SetDragAccept;
end;
procedure TDragDrop.SetDragAccept;
begin
if FControl<>nil then
begin
if FAccept then
begin
//if accept=true, subclass default window procedure
OldWndProc:=FControl.WindowProc;
FControl.WindowProc:=WndProc;
end else
FControl.WindowProc:=OldWndProc;
DragAcceptFiles(FControl.Handle,FAccept);
end;
end;
procedure TDragDrop.SetOnDragDropFiles(const Value: TDragDropFilesEvent);
begin
FOnDragDropFiles := Value;
end;
procedure TDragDrop.WndProc(var msg: TMessage);
begin
//msg.WParam berisi handle Drop struktur
if msg.Msg=WM_DROPFILES then
begin
AcceptFiles(msg.WParam);
msg.Result:=0;
end else
OldWndProc(msg);
end;
end.
Usage sample: Create new application. Drop TListbox from Component Palette onto the main form. Create event handler for OnCreate event. Flesh out FormCreate method to make it look like following code:
procedure TForm1.FormCreate(sender:TObject);
begin
DragDrop1:=TDragDrop.Create(self);
DragDrop1.OnDragDropFiles:=DragDropFiles;
DragDrop1.Control:=ListBox1;
DragDrop1.Accept:=true;
end;
procedure TForm1.DragDropFiles(Sender: TObject; const FileIndex,
TotalFile: Integer; const Filename: String);
begin
ListBox1.Items.Add(Filename);
end;
Everytime one or more files dragged and dropped from Windows Explorer onto ListBox1, then filenames will be listed in ListBox1. Click here to download source code.
In this article, I have explained how to use DragAcceptFiles(), DragQueryFile() and DragFinish() to handle drag drop operation from Windows Explorer onto our own control. I also explained how to implement this functionalities into a component to wrap and simplify drag drop operation.
Do you like this article? Help this website improve by donating. Any amounts is appreciated.
Or you can help by bookmarking this page.
Bookmark this on Delicious