Recent

Author Topic: Drag&drop from Lazarus app to Windows Explorer  (Read 861 times)

domasz

  • Hero Member
  • *****
  • Posts: 553
Drag&drop from Lazarus app to Windows Explorer
« on: September 04, 2024, 06:36:10 pm »
I have found a nice component that allows me to drag&drop files from Lazarus app to Windows Explorer. It used QueryContinueDrag and it works.
I also have used a Lazarus built-it method to accept files dragged&dropped from Windows Explorer. This works too.

But now it also is possible to drag&drop files from my Lazarus app to my Lazarus app- and I don't know how to block it.

Full source code attached.

440bx

  • Hero Member
  • *****
  • Posts: 4740
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #1 on: September 04, 2024, 07:41:25 pm »
But now it also is possible to drag&drop files from my Lazarus app to my Lazarus app- and I don't know how to block it.
The solution that comes to mind is to use GetCapture to determine which app has the captured the mouse (drag and drop requires capturing the mouse.)

Once you've determined the app that is dragging, it is simple to refuse the operation in the DragEnter method by setting the drop effect.

Basically, in the DragEnter method, call GetCapture which returns the window that has captured the mouse, therefore is doing the drag and drop, enumerate the windows looking for the window handle that matches the one of from GetCapture, once that handle is found, use GetWindowModuleFilename to determine the executable that is doing the drag.  Based on the filename determine if the drag-and-drop should be allowed.

HTH.

ETA:

fixed grammatical error.

« Last Edit: September 04, 2024, 09:44:18 pm by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

domasz

  • Hero Member
  • *****
  • Posts: 553
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #2 on: September 04, 2024, 08:16:19 pm »
Thank you, 440bx. Works great.
Code: Pascal  [Select][+][-]
  1.     Window: HWND;
  2.     ProcessID: Dword;
  3.     H: Handle;
  4.     buffer: array[0..MAX_PATH-1] of WideChar;
  5.     Size: DWord;
  6. begin
  7.  
  8.   Window := GetCapture;
  9.   GetWindowThreadProcessId(Window, ProcessId);
  10.   H := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, ProcessID);
  11.   size := MAX_PATH;
  12.   Windows.QueryFullProcessImageNameW(H, 0, buffer, @Size);
  13.   ShowMessage(buffer);

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1430
    • Lebeau Software
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #3 on: September 04, 2024, 08:41:56 pm »
The solution that comes to mind is to use GetCapture to determine which app has the captured the mouse (drag and drop requires capturing the mouse.)

Once you've determined the app that is dragging, it is simple to refuse the operation in the DragEnter method by setting the drop effect.

Another solution that comes to my mind is to store an extra data marker inside the dragged IDataObject to indicate the drag is coming from your app, and then have your target's DragEnter query for that marker.  An IDataObject can hold multiple data formats at a time, even custom formats.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

domasz

  • Hero Member
  • *****
  • Posts: 553
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #4 on: September 04, 2024, 09:07:30 pm »
Well, I was too quick- it doesn't really work.

The code based on 440bx answer does return app exe when invoked from the TDragDropSource class. But that class is supposed to drop files to Explorer so it always returns Project1.exe.

When I put that code in Form1.FormDropFiles it always returns nothing - GetCapture returns 0.

domasz

  • Hero Member
  • *****
  • Posts: 553
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #5 on: September 04, 2024, 09:08:28 pm »
I don't have IDataObject in Form1.FormDropFiles and don't know how to find it.

440bx

  • Hero Member
  • *****
  • Posts: 4740
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #6 on: September 04, 2024, 09:41:51 pm »
When I put that code in Form1.FormDropFiles it always returns nothing - GetCapture returns 0.
It's likely the problem is that the drag operation has ended once the files have been dropped which is when DropFiles gets invoked, therefore it's normal that GetCapture would return 0.

IOW, you have to call GetCapture _before_ the files are dropped and use that code you showed to determine which app is doing the drag and drop.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1430
    • Lebeau Software
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #7 on: September 04, 2024, 11:53:25 pm »
I don't have IDataObject in Form1.FormDropFiles and don't know how to find it.

The OnDropFiles event is fired in response to a WM_DROPFILES window message.  But all drag&drop operations in Windows are ultimately based on IDragSource/IDropTarget and IDataObject. It just happens that if you don't implement IDropTarget in your app then Windows will automatically query a dragged IDataObject for CF_HDROP data and if found then it will generate a WM_DROPFILES message for that data.  So no, you don't have access to the original IDataObject in that situation.  You need to implement IDropTarget in order to access a dragged IDataObject.

You said you were using QueryContinueDrag() when dragging TO Explorer.  That is a method of IDragSource, which requires your app to provide an IDataObject when starting a drag operation.  In which case, you likely DO have access to an IDataObject when dragging OUT of your app, so you would just need to add IDropTarget to receive the same IDataObject when dragging IN to your app (which you should do anyway, because WM_DROPFILES is ancient, dating way back to the Windows 3.x days).

@440bx's answer is also based on the assumption that you were implementing IDropTarget in your app, as the mentioned DragEnter() method is part of IDropTarget.
« Last Edit: September 05, 2024, 05:57:40 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

domasz

  • Hero Member
  • *****
  • Posts: 553
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #8 on: September 05, 2024, 12:06:59 pm »
Thanks! Works now.

Uses Drag Drop library available in OPM:
Code: Pascal  [Select][+][-]
  1. unit Main;
  2.  
  3. {$MODE Delphi}
  4.  
  5. interface
  6.  
  7. uses
  8.   DragDrop,
  9.   DropTarget,
  10.   DragDropFile,
  11.   LCLIntf, LCLType, LMessages, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  12.   StdCtrls, ComCtrls, ExtCtrls, DropSource, Types, Windows, JwaPsApi;
  13.  
  14. type
  15.  
  16.   { TForm1 }
  17.  
  18.   TForm1 = class(TForm)
  19.     DropDummy1: TDropDummy;
  20.     DropFileSource1: TDropFileSource;
  21.     DropFileTarget1: TDropFileTarget;
  22.     ListView1: TListView;
  23.     procedure DropFileSource1GetDragImage(Sender: TObject;
  24.       const DragSourceHelper: IDragSourceHelper; var Handled: boolean);
  25.     procedure DropFileTarget1Drop(Sender: TObject; ShiftState: TShiftState;
  26.       Point: TPoint; var Effect: Integer);
  27.     procedure DropFileTarget1Enter(Sender: TObject; ShiftState: TShiftState;
  28.       APoint: TPoint; var Effect: Longint);
  29.     procedure ListView1MouseDown(Sender: TObject; Button: TMouseButton;
  30.       Shift: TShiftState; X, Y: Integer);
  31.   private
  32.     { Private declarations }
  33.   public
  34.     { Public declarations }
  35.   end;
  36.  
  37. var
  38.   Form1: TForm1;
  39.  
  40. implementation
  41.  
  42. uses  ActiveX, CommCtrl;
  43.  
  44. {$R *.lfm}
  45.  
  46. procedure TForm1.DropFileSource1GetDragImage(Sender: TObject;
  47.   const DragSourceHelper: IDragSourceHelper; var Handled: boolean);
  48. var
  49.   Pt: TPoint;
  50. begin
  51.   GetCursorPos(Pt);
  52.   Handled := Succeeded(DragSourceHelper.InitializeFromWindow(Listview1.Handle, Pt, TCustomDropSource(Sender) as IDataObject));
  53. end;
  54.  
  55. procedure TForm1.DropFileTarget1Drop(Sender: TObject;
  56.   ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
  57. var
  58.   i: integer;
  59.   Item: TListItem;
  60. begin
  61.   // called when the user drag and drop files onto your application.
  62.  
  63.   // Display mapped names if present.
  64.   // Mapped names are usually only present when dragging from the recycle bin
  65.   // (try it).
  66.   if (DropFileTarget1.MappedNames.Count > 0) then
  67.     Listview1.Columns[1].Width := 100
  68.   else
  69.     Listview1.Columns[1].Width := 0;
  70.  
  71.   // Copy the file names from the DropTarget component into the list view.
  72.   for i := 0 to DropFileTarget1.Files.Count-1 do
  73.   begin
  74.     Item := Listview1.Items.Add;
  75.     Item.Caption := DropFileTarget1.Files[i];
  76.  
  77.     // Display mapped names if present.
  78.     if (DropFileTarget1.MappedNames.Count > i) then
  79.       Item.SubItems.Add(DropFileTarget1.MappedNames[i]);
  80.   end;
  81.  
  82.   // Reject "moved" files
  83.   if (Effect = DROPEFFECT_MOVE) then
  84.     Effect := DROPEFFECT_NONE;
  85. end;
  86.  
  87. procedure TForm1.DropFileTarget1Enter(Sender: TObject; ShiftState: TShiftState;
  88.   APoint: TPoint; var Effect: Longint);
  89. var
  90. Window: HWND;
  91. ProcessID: Dword;
  92. H: Handle;
  93. buffer: array[0..MAX_PATH-1] of WideChar;
  94. Size: DWord;
  95. AppExe: String;
  96. begin
  97.  
  98. Window := GetCapture;
  99.  
  100. if Window = 0 then Exit;
  101.  
  102. GetWindowThreadProcessId(Window, ProcessId);
  103. H := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, ProcessID);
  104. size := MAX_PATH;
  105. Windows.QueryFullProcessImageNameW(H, 0, buffer, @Size);
  106. AppExe := buffer;
  107.  
  108. if AppExe = Application.ExeName then Effect := DROPEFFECT_NONE;
  109. end;
  110.  
  111.  
  112. procedure TForm1.ListView1MouseDown(Sender: TObject; Button: TMouseButton;
  113.   Shift: TShiftState; X, Y: Integer);
  114. var i: integer;
  115. begin
  116. // Wait for user to move mouse before we start the drag/drop.
  117.  
  118. // Note:
  119. // Due to some internal mouse message juggling inside TListView we will not
  120. // get the MouseDown event until the mouse is either moved or the mouse button
  121. // is released.
  122. // Remember this when it appears that DragDetectPlus isn't working...
  123. if (Listview1.SelCount > 0) and (DragDetectPlus(TWinControl(Sender))) then
  124. begin
  125.   // Delete anything from a previous drag.
  126.   DropFileSource1.Files.Clear;
  127.  
  128.   // Fill DropSource1.Files with selected files from ListView1.
  129.   for i := 0 to Listview1.Items.Count-1 do
  130.     if (Listview1.items.Item[i].Selected) then
  131.       DropFileSource1.Files.Add(Listview1.items.Item[i].Caption);
  132.  
  133.   // Start the drag operation.
  134.   DropFileSource1.Execute;
  135. end;
  136. end;
  137.  
  138. end.

Zvoni

  • Hero Member
  • *****
  • Posts: 2747
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #9 on: September 05, 2024, 12:27:44 pm »
Huh?
I would have expected a simple Form-wide boolean-variable being enough
Kinda like
Code: Pascal  [Select][+][-]
  1. TForm1=Class(TForm)
  2. .....
  3. .....
  4. Private
  5.    IsDragSource:Boolean;
  6. ....
  7. End;
That variable is always (initialized) to False, except being set to True in OnStartDrag.
In OnDragDrop/OnDropFiles you check if this Var is True.
If True, you don't do anything, except setting the Var to False again.
If False, then the "Source" of the Drag-Opration was outside your Form/Control/Whatever

But since Remy answered, i might be way left Field of reality.... so.... *shrug*
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

domasz

  • Hero Member
  • *****
  • Posts: 553
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #10 on: September 05, 2024, 12:35:14 pm »
The program does 3 things:
- allows you to drag&drop files from Explorer
- allows you to drag&drop files to Explorer
- blocks dragging&dropping from itself to itself
So a simple Boolean value is not enough.

Zvoni

  • Hero Member
  • *****
  • Posts: 2747
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #11 on: September 05, 2024, 12:53:35 pm »
Quote
- allows you to drag&drop files from Explorer
IsDragSource=False --> Process the code

Quote
- allows you to drag&drop files to Explorer
IsDragSource=True --> Check the Sender in OnDragDrop if it's a Component of your Form, if not.....

Quote
- blocks dragging&dropping from itself to itself
IsDragSource=True --> Check the Sender in OnDragDrop if it's a Component of your Form, if yes.....


But i agree with you: You have something working now.... --> Full steam ahead
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1430
    • Lebeau Software
Re: Drag&drop from Lazarus app to Windows Explorer
« Reply #12 on: September 05, 2024, 06:01:42 pm »
Huh?
I would have expected a simple Form-wide boolean-variable being enough

That would not work when dragging from one instance of the app to another instance of the same app.  Unless you store that boolean in shared memory.

In which case, it would be better to just put the boolean (or whatever other kind of marker you want) inside of the dragged IDataObject itself.  Though, TDropFileSource/TDropFileTarget probably don't support extra custom data, but TDropEmptySource/TDropEmptyTarget do (via data format adapters).
« Last Edit: September 05, 2024, 06:08:06 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018