Lazarus

Programming => General => Topic started by: pcurtis on September 28, 2020, 01:43:30 pm

Title: [SOLVED] Copy file to clipboard
Post by: pcurtis on September 28, 2020, 01:43:30 pm
Hi All,

How do I copy a file to the clipboard?

Thanks in advance.
Title: Re: Copy file to clipvoard
Post by: Thaddy on September 28, 2020, 01:46:10 pm
 E/g Tmemo.copytoclipboard?
Title: Re: Copy file to clipvoard
Post by: pcurtis on September 28, 2020, 02:29:11 pm
No, something like CopyToClipboad('c:\myfile.xml');
Title: Re: Copy file to clipvoard
Post by: rvk on September 28, 2020, 02:35:44 pm
No, something like CopyToClipboad('c:\myfile.xml');
Something like this
https://forum.lazarus.freepascal.org/index.php?topic=18637.0
or this
https://stackoverflow.com/questions/27278659/copy-a-file-to-clipboard-in-delphi
Title: Re: Copy file to clipboard
Post by: pcurtis on September 28, 2020, 03:13:47 pm
I've tried

Code: Pascal  [Select][+][-]
  1. uses
  2.   Windows, ShlObj, Clipbrd;
  3.  
  4. procedure CopyFileToClipboard(FileList : string);
  5. var
  6.   DropFiles: PDropFiles;
  7.   hGlobal: THandle;
  8.   iLen: integer;
  9. begin
  10.   iLen := Length(FileList) + 2;
  11.   FileList := FileList + #0#0;
  12.   hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
  13.     SizeOf(TDropFiles) + iLen);
  14.   if (hGlobal = 0) then
  15.     raise Exception.Create('Could not allocate memory.');
  16.   begin
  17.     DropFiles := GlobalLock(hGlobal);
  18.     DropFiles^.pFiles := SizeOf(TDropFiles);
  19.     Move(FileList[1], (PChar(DropFiles) + SizeOf(TDropFiles))^, iLen);
  20.     GlobalUnlock(hGlobal);
  21.     OpenClipboard(Form1.Handle);
  22.     EmptyClipboard;
  23.     SetClipboardData(CF_HDROP,hGlobal);
  24.     CloseClipboard;
  25.    end;
  26. end;
  27.  
  28. procedure TForm1.btnPasteClick(Sender: TObject);
  29. begin
  30.   CopyFileToClipboard('d:\Lazarus\Icons\bug-64.ico');
  31. end;
  32.  

And can't get it to work.
Title: Re: Copy file to clipboard
Post by: rvk on September 28, 2020, 03:26:39 pm
And can't get it to work.
Works perfectly fine for me (just tested it)... but "can't get it to work" isn't a valid problem description.
Title: Re: Copy file to clipboard
Post by: pcurtis on September 28, 2020, 03:28:47 pm
I am trying emulate this :

1. Open Explorer and navigate to an image (test.xml for example)
2. Press CTRL+C
3. Open Wordpad
4. Press CTRL+V, and the image (test.xml in this example) appears
Title: Re: Copy file to clipboard
Post by: rvk on September 28, 2020, 03:36:23 pm
I am trying emulate this :

1. Open Explorer and navigate to an image (test.xml for example)
2. Press CTRL+C
3. Open Wordpad
4. Press CTRL+V, and the image (test.xml in this example) appears
Ah, that's better.
The code you showed works fine in Windows Explorer. Run it, go to a empty folder and press CTRL+V (or right click and paste).
You see that it works for copying files in Explorer.

For pasting in Wordpad you need to copy it a different way.
Try a Clipboard viewer to see what's on the clipboard when you copy a file.
You'll see a FileList entry (which is for pasting in Explorer etc) and another format.
You need to emulate that format (I'm currently not sure what format that is, probably a CF_OBJECT - Any persistent object).
(Or with CFSTR_FILECONTENTS and CFSTR_FILEDESCRIPTOR)
Title: Re: Copy file to clipboard
Post by: pcurtis on September 28, 2020, 03:48:32 pm
I can confirm that for copying the physical file it works.

I'm not sure what you mean by

Quote
You need to emulate that format (I'm currently not sure what format that is, probably a CF_OBJECT - Any persistent object).
(Or with CFSTR_FILECONTENTS and CFSTR_FILEDESCRIPTOR)
Title: Re: Copy file to clipboard
Post by: rvk on September 28, 2020, 04:20:05 pm
I can confirm that for copying the physical file it works.
I'm not sure what you mean by
Quote
You need to emulate that format (I'm currently not sure what format that is, probably a CF_OBJECT - Any persistent object).
(Or with CFSTR_FILECONTENTS and CFSTR_FILEDESCRIPTOR)
Described as second method here: https://stackoverflow.com/a/44509407/1037511

Not sure if The Drag and Drop Component Suite for Delphi can be used or is already ported to Lazarus/FPC/
https://github.com/DelphiPraxis/The-Drag-and-Drop-Component-Suite-for-Delphi
https://www.freepascal.org/~michael/articles/dragdrop2/dragdrop2.pdf

Ah, there is a Fork: https://forum.lazarus.freepascal.org/index.php/topic,38761.msg264283.html#msg264283
Not sure if it compiles in current version.
Title: Re: Copy file to clipboard
Post by: pcurtis on September 28, 2020, 05:38:46 pm
OK.

Where or how do I get  CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS?
Title: Re: Copy file to clipboard
Post by: rvk on September 28, 2020, 08:03:20 pm
Where or how do I get  CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS?
Code: Pascal  [Select][+][-]
  1. begin
  2.   CF_Names:=RegisterClipBoardFormat(CFSTR_FILEDESCRIPTOR);  
  3.   CF_Contents:=RegisterClipBoardFormat(CFSTR_FILECONTENTS);
  4. end;
but that's not the only thing you need if you look at the links I gave you.

Quote
2. use CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS instead of CF_HDROP. Then you can provide an IStream to your data (look at the RTL's TStreamAdapter class for wrapping a TStream inside of an IStream implementation). However, you cannot do this with SetClipboardData(), you would need to implement the IDataObject interface and use it with OleSetClipboard() instead.

It's also in the Drag and Drop Component Suite but I don't know how that one works for copying files to clipboard.
But you will always need to provide the data when the file is pasted (for Explorer it works because that one is alway in the air).

What are you using this for exactly????
It might be easier to issue a copy command to explorer.exe or Windows itself.
As an example this code from here (https://stackoverflow.com/a/26000424/1037511) works fine in Delphi.
It uses OleSetClipboard() to put the file descriptors on the clipboard and will paste fine to Wordpad.

Code: Pascal  [Select][+][-]
  1. uses
  2.   System.Classes, Winapi.Windows, Winapi.ActiveX, Winapi.Shlobj, Winapi.ShellAPI, System.Win.ComObj;
  3.  
  4. procedure CopyFilesToClipboard(const Folder: string; FileNames: TStrings);
  5. var
  6.   SF: IShellFolder;
  7.   PidlFolder: PItemIDList;
  8.   PidlChildren: array of PItemIDList;
  9.   Eaten: UINT;
  10.   Attrs: DWORD;
  11.   Obj: IDataObject;
  12.   I: Integer;
  13. begin
  14.   if (Folder = '') or (FileNames = nil) or (FileNames.Count = 0) then Exit;
  15.   OleCheck(SHParseDisplayName(PChar(Folder), nil, PidlFolder, 0, Attrs));
  16.   try
  17.     OleCheck(SHBindToObject(nil, PidlFolder, nil, IShellFolder, Pointer(SF)));
  18.   finally
  19.     CoTaskMemFree(PidlFolder);
  20.   end;
  21.   SetLength(PidlChildren, FileNames.Count);
  22.   for I := Low(PidlChildren) to High(PidlChildren) do
  23.     PidlChildren[i] := nil;
  24.   try
  25.     for I := 0 to FileNames.Count-1 do
  26.       OleCheck(SF.ParseDisplayName(0, nil, PChar(FileNames[i]), Eaten, PidlChildren[i], Attrs));
  27.     OleCheck(SF.GetUIObjectOf(0, FileNames.Count, PIdlChildren[0], IDataObject, nil, obj));
  28.   finally
  29.     for I := Low(PidlChildren) to High(PidlChildren) do
  30.     begin
  31.       if PidlChildren[i] <> nil then
  32.         CoTaskMemFree(PidlChildren[i]);
  33.     end;
  34.   end;
  35.   OleCheck(OleSetClipboard(obj));
  36.   OleCheck(OleFlushClipboard);
  37. end;
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. var
  41.   FileNames: TStringList;
  42. begin
  43.   FileNames := TStringList.Create;
  44.   try
  45.     FileNames.Add('test.txt');
  46.     CopyFilesToClipboard('D:\', FileNames);
  47.   finally
  48.     FileNames.Free;
  49.   end;
  50. end;
Title: Re: Copy file to clipboard
Post by: pcurtis on September 28, 2020, 08:29:49 pm
I want to use it to put an image into a Richedit component.

Richedit doesn't support images directly, but you can copy/paste them.

How would / could I compile the above code in Laz?

And thanks for your time and help.
Title: Re: Copy file to clipboard
Post by: rvk on September 28, 2020, 08:43:19 pm
How would / could I compile the above code in Laz?
I don't know how to implement OleSetClipboard in Lazarus.
The only reference to OleSetClipboard I found for laz was in virtualtreeview.
You could look at that one to see how it's implemented.
(Can't look at it myself right now)

I want to use it to put an image into a Richedit component.
Richedit doesn't support images directly, but you can copy/paste them.
Richedit does support images.
What component are you using?

For example https://wiki.lazarus.freepascal.org/RichMemo#InsertImageFromFile

Another option could be to insert raw rtf data containing the picture.
https://stackoverflow.com/a/1490785/1037511
Title: Re: Copy file to clipboard
Post by: pcurtis on September 28, 2020, 08:58:38 pm
The problem with InsertImageFromFile is that the image isn't rendered (just an icon appears), but works with copy / paste.
Title: Re: Copy file to clipboard
Post by: rvk on September 28, 2020, 09:07:17 pm
The problem with InsertImageFromFile is that the image isn't rendered (just an icon appears), but works with copy / paste.
Is that also true if you safe and reload the file?
If that's the case it is inserted as object, not as image.

Does pasting a file in richmemo work?
I only tried a text document and that also just got an icon in wordpad.
Will test further tomorrow.
Title: Re: Copy file to clipboard
Post by: pcurtis on September 28, 2020, 10:25:34 pm
Quote
Is that also true if you safe and reload the file?
If that's the case it is inserted as object, not as image.
Yes

Quote
Does pasting a file in richmemo work?
I only tried a text document and that also just got an icon in wordpad.
Will test further tomorrow.

Copy / paste only seems to work for images, text files create an icon.
Title: Re: Copy file to clipboard
Post by: Remy Lebeau on September 28, 2020, 10:45:18 pm
I don't know how Lazarus's TRichEdit works, but in a standard Win32 RichEdit control (such as wrapped in Delphi's TRichEdit), you insert images by using the EM_GETOLEINTERFACE (https://docs.microsoft.com/en-us/windows/win32/controls/em-getoleinterface) message to get the RichEdit's IRichEditOle (https://docs.microsoft.com/en-us/windows/win32/api/richole/nn-richole-iricheditole) interface, and then call its InsertObject() (https://docs.microsoft.com/en-us/windows/win32/api/richole/nf-richole-iricheditole-insertobject) method, giving it an IOleObject interface for the image data.  See How to Use OLE in Rich Edit Controls (https://docs.microsoft.com/en-us/windows/win32/controls/using-rich-edit-com).
Title: Re: Copy file to clipboard
Post by: jamie on September 28, 2020, 11:11:10 pm
Is this all about the shell operation of dragging and dropping file list of names and where as the receiving window needs to be registered to accept or receive WM_DROPFILE messages ?

 If this is the case, the TFORM has a OnDropFiles event, you need to enable it. "AllowDropFiles"..

 If you are trying to initiate a drop operation then let me know, cause I think I have the code somewhere around here for that.
Title: Re: Copy file to clipboard
Post by: pcurtis on September 29, 2020, 02:41:12 am
OK All I think it's time for a recap.

1. Get a clipboard viewer. I use https://www.freeclipboardviewer.com/FreeClipViewer.zip (https://www.freeclipboardviewer.com/FreeClipViewer.zip)

2. Run the program.

3. Open Explorer, and find an image file and copy it to the clipboard.

4. Note the changes in the clipboard viewer.

5. Open Wordpad. You should be able to paste the previously copied image into Wordpad.

6. My opening question was

Quote
Hi All,
How do I copy a file to the clipboard?

To which it was suggested to try the following code

Code: Pascal  [Select][+][-]
  1. procedure CopyFileToClipboard(FileList : string);
  2. var
  3.   DropFiles: PDropFiles;
  4.   hGlobal: THandle;
  5.   iLen: integer;
  6.   CFSTR_FILEDESCRIPTOR, CFSTR_FILECONTENTS : UINT;
  7. begin
  8.   //CFSTR_FILEDESCRIPTOR := RegisterClipboardFormat();
  9.   iLen := Length(FileList) + 2;
  10.   FileList := FileList + #0#0;
  11.   hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
  12.     SizeOf(TDropFiles) + iLen);
  13.   if (hGlobal = 0) then
  14.     raise Exception.Create('Could not allocate memory.');
  15.   begin
  16.     DropFiles := GlobalLock(hGlobal);
  17.     DropFiles^.pFiles := SizeOf(TDropFiles);
  18.     Move(FileList[1], (PChar(DropFiles) + SizeOf(TDropFiles))^, iLen);
  19.     GlobalUnlock(hGlobal);
  20.     OpenClipboard(Form1.Handle);
  21.     EmptyClipboard;
  22.     SetClipboardData(CF_HDROP, hGlobal);
  23.     CloseClipboard;
  24.    end;
  25. end;
  26.  
  27.  
  28.  
  29. procedure TForm1.Button1Click(Sender: TObject);
  30. begin
  31.   CopyFileToClipboard('d:\Lazarus\Icons\bug-64.ico');
  32. end;  
  33.  

Which copies a list of files to the clipboard. Which is NOT what I need.

I need a way to copy the file as in step 1.

I was advised to look at https://docs.microsoft.com/en-us/windows/win32/controls/using-rich-edit-com (https://docs.microsoft.com/en-us/windows/win32/controls/using-rich-edit-com) which looks promising but I don't speak 'C'.
Title: Re: Copy file to clipboard
Post by: rvk on September 29, 2020, 11:12:33 am
Ok, this might work (it worked for me).

Code: Pascal  [Select][+][-]
  1. uses Windows, ShlObj, ComObj, ActiveX;
  2.  
  3. function SHBindToObject(const psf: IShellFolder; pidl: PItemIDList;
  4.   const pbc: IBindCtx; const riid: TIID; var ppv: Pointer): HResult; stdcall; external 'shell32' name 'SHBindToObject';
  5.  
  6. procedure CopyFilesToClipboard2(const Folder: widestring; FileNames: TStrings);
  7. var
  8.   SF: IShellFolder;
  9.   PidlFolder: PItemIDList;
  10.   PidlChildren: array of PItemIDList;
  11.   Eaten: UINT;
  12.   Attrs: DWORD;
  13.   Obj: IDataObject;
  14.   I: Integer;
  15.   Filename: widestring;
  16. begin
  17.   if (Folder = '') or (FileNames = nil) or (FileNames.Count = 0) then Exit;
  18.   OleCheck(SHParseDisplayName(PWideChar(Folder), nil, PidlFolder, 0, @Attrs));
  19.   try
  20.     OleCheck(SHBindToObject(nil, PidlFolder, nil, IShellFolder, Pointer(SF)));
  21.   finally
  22.     CoTaskMemFree(PidlFolder);
  23.   end;
  24.   SetLength(PidlChildren, FileNames.Count);
  25.   for I := Low(PidlChildren) to High(PidlChildren) do
  26.     PidlChildren[i] := nil;
  27.   try
  28.     for I := 0 to FileNames.Count-1 do
  29.     begin
  30.       Filename := Filenames[i];
  31.       OleCheck(SF.ParseDisplayName(0, nil, PWideChar(FileName), Eaten, PidlChildren[i], Attrs));
  32.  
  33.     end;
  34.     OleCheck(SF.GetUIObjectOf(0, FileNames.Count, PIdlChildren[0], IDataObject, nil, obj));
  35.   finally
  36.     for I := Low(PidlChildren) to High(PidlChildren) do
  37.     begin
  38.       if PidlChildren[i] <> nil then
  39.         CoTaskMemFree(PidlChildren[i]);
  40.     end;
  41.   end;
  42.   OleCheck(OleSetClipboard(obj));
  43.   OleCheck(OleFlushClipboard);
  44. end;
  45.  
  46. procedure TForm1.Button1Click(Sender: TObject);
  47. var
  48.   FileNames: TStringList;
  49. begin
  50.   FileNames := TStringList.Create;
  51.   try
  52.     FileNames.Add('file1.jpg');
  53.     CopyFilesToClipboard2('C:\Your_directory_with_the_files', FileNames);
  54.   finally
  55.     FileNames.Free;
  56.   end;
  57. end;
Title: Re: Copy file to clipboard
Post by: pcurtis on September 29, 2020, 12:08:07 pm
rvk - Your the man! It works perfectly.

Thank you so much.
TinyPortal © 2005-2018