0
votes

I'm using Delphi 10.3 Community Edition. I'm trying to drag and drop files from a Windows folder onto my application but the Windows message handler is not called when I drag and drop a file on the form.

This is what I have at the moment:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  protected
    procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
    ShellApi;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin

    // Disable drag accept files
    DragAcceptFiles(Self.Handle, true);

end;

procedure TForm1.FormCreate(Sender: TObject);
begin

    // Enable drag accept files
    DragAcceptFiles(Self.Handle, true);

end;

procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
begin

    // Show a message
    ShowMessage('File dropped');

    // Set the message result
    Msg.Result := 0;

    inherited;

end;

end.

Like I said, when I drag and drop a file on the form, I can see that the file is accepted when dragged onto the form but when I drop the file, the WMDropFiles procedure is not called.

I also tried enabling the DragAcceptFiles in the CreateWnd procedure. But it still does not work.

...
public
    procedure CreateWnd; override;
    procedure DestroyWnd; override;

...

procedure TForm1.CreateWnd;
begin

  inherited;
  DragAcceptFiles(Handle, True);

end;

procedure TForm1.DestroyWnd;
begin

  DragAcceptFiles(Handle, False);
  inherited;

end;

I even tried running the Delpi IDE as Administrator. Could it be a limitation of the Community Edition or am I missing something?

Addendum

I've now added a button to send a message WM_DROPFILES.

procedure TForm1.Button1Click(Sender: TObject);
begin
    SendMessage(Self.Handle, WM_DROPFILES, Integer(self), 0);
end;

When I click the button, the WMDropFiles procedure is called. So then it works.

2
FYI, the preferred way to accept dropped data, including files, is to implement the IDropTarget interface. WM_DROPFILES is a legacy deprecated API. See Transferring Shell Objects with Drag-and-Drop and the Clipboard. Anders Melander has an excellent Drag&Drop suite for Delphi which implements IDropTarget for you.Remy Lebeau
@RemyLebeau Hi Remy, long time since I've last seen you!! Think it was Indy Sockets?bluscape

2 Answers

0
votes

Ok, very interesting. I found this article:

How to Enable Drag and Drop for an Elevated MFC Application on Vista/Windows 7

So I added the following to my form create procedure:

procedure TForm1.FormCreate(Sender: TObject);
begin


    // Enable drag accept files
    DragAcceptFiles(Form1.Handle, true);

    ChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD);
    ChangeWindowMessageFilter (WM_COPYDATA, MSGFLT_ADD);
    ChangeWindowMessageFilter ($0049, MSGFLT_ADD);

end;

And now it is working!

-1
votes

I changed the topic to include text "TListView" because I actually want to drop files on a TListView. Since I've solved the problem with dropping files on a form, I still had an issue with dropping files on a TListView.

So to drop the files on a TListView, you have to change the Handle to the listview's handle:

// Enable drag accept files
DragAcceptFiles(MyListview.Handle, true);

and

// Disable drag accept files
DragAcceptFiles(MyListview.Handle, false);

But that alone is not enough. You then need a Application events handler to catch the messages and handle them accordingly. So I just added a TApplicationEvents component to the form and added the following to the OnMessage event:

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
begin

    // If it is a drop files message and it is for the listview
    if ((Msg.message = WM_DROPFILES)and (Msg.hwnd = MyListView.Handle)) then
    begin

        // Handle your dropped data here

    end;

end;