First: my goal is read if a file(s)/folder(s) gets copy in another app and read the file/folder name(s) in my app.
The copy IS NOT a file name or folder name. It is the file o folder itself (by example in Windows Explorer, select a file and Crtl+C). Meaning what the Format of Clipboard would by CF_HDROP.
No, it is NOT the actual file/folder(s) themselves, it is indeed just their file/folder path strings. The CF_HDROP format consists of a
DROPFILES structure that points to a double null-terminated list of file/folder location strings, nothing more.
By design, a pointer to a DROPFILES structure can be used as-is as an HDROP handle for the DragQuery...() API functions for easy access to the list values. Or, you could just parse the string list directly.
See
Shell Clipboard Formats | CF_HDROP for more details.
The first problem I find is:
hDropHandle := Clipboard.GetAsHandle(CF_HDROP)
because this method GetAsHandle not exist in Lazarus.
No, but it does have a
GetFormat() method that reads the clipboard data into a TStream.
so I replace with:
hDropHandle := GetClipboardData(CF_HDROP);
The problem is what that NEVER give the Handle of the Clipboard, always return 0.
Another problem I see debugging the code is this line:
if (Clipboard.HasFormat(CF_HDROP)) then
In Delphi if I copy a file(s) in Explorer the sentence is TRUE because the Clipboard has the format CF_HDROP, but I do the SAME in Lazarus and that sentence is always FALSE not matter what copy to the clipboard.
I can't find FreePascal's source code for TClipboard, but it
sounds like TClipboard.Open() is clearing the current clipboard content (by calling
EmptyClipboard()). In Delphi, TClipboard.Open() does not do that.
Well, apparently the problem was:
I comment that line and magically:
if (Clipboard.HasFormat(CF_HDROP)) then
works as intended.
That would fall inline with TClipboard.Open() clearing the clipboard. The clipboard does not need to be opened before querying/enumerating it for available formats, only for reading/writing format data.
The problem however still was:
hDropHandle := GetClipboardData(CF_HDROP);
still dont give the handle of the clipboard of the CF_HDROP format...
That would fall inline with TClipboard.Open() clearing the clipboard.
So if anyone are interested the procedure is like this:
You shouldn't be calling CloseClipboard() unless OpenClipboard() is successful first. Move your calls to Clipboard.HasFormat() and OpenClipboard() out of your try/finally block:
procedure Sto_PasteFilenamesFromClipboard(Filenames: TStrings);
var
hDropHandle: HDROP;
szBuffer: PChar;
iCount, iIndex: Integer;
iLength: Integer;
begin// check entry conditions
if (Filenames = nil) then
Exit;
Filenames.BeginUpdate;
try
Filenames.Clear;
// does clipboard contain filenames?
if not Clipboard.HasFormat(CF_HDROP) then
Exit;
if not OpenClipboard(0) then // lock clipboard
Exit; // or raise an exception
try
// get drop handle from the clipboard
hDropHandle := GetClipboardData(CF_HDROP);
if hDropHandle = 0 then
Exit; // or raise an exception
iCount := DragQueryFile(hDropHandle, $FFFFFFFF, nil, 0);
for iIndex := 0 to iCount - 1 do
begin
// get length of filename
iLength := DragQueryFile(hDropHandle, iIndex, nil, 0);
// allocate the memory, the #0 is not included in "iLength"
szBuffer := StrAlloc(iLength + 1);
try
// get filename
if DragQueryFile(hDropHandle, iIndex, szBuffer, iLength + 1) > 0 then
Filenames.Add(szBuffer);
finally
// free the memory
StrDispose(szBuffer);
end;
end;
finally
// unlock clipboard
CloseClipboard;
end;
finally
Filenames.EndUpdate;
end;
end;