Skip to content
chrisrolliston edited this page Jun 7, 2015 · 1 revision

A 'virtual file' is data put on the clipboard that may be pasted as a file, but isn't yet on the file system. Virtual files are formally supported on both Windows and OS X, however on a Mac, Finder does not support pasting virtual files, only having them dragged to it (Explorer on Windows works both ways). Nevetherless, if an application is set up to look for virtual files on the clipboard, then the support provided by TClipboard should work as a paste source, as well as a copying destination.

Copying

Both immediate and delayed rendering is supported when copying a virtual file. In either case you should pass a made-up file name (without a path) to identify the item:

//assign a TBytes instance
Clipboard.AssignVirtualFile('Example.dat', MyBytes);
//assign a delay-rendered TBytes instance
Clipboard.AssignVirtualFileDelayed('Example.dat', 
  function : TBytes
  begin
    Result := //...
  end);
//assign a IStreamPersist implementation, here a FMX TBitmap
Clipboard.AssignVirtualFile('Example.png', Image1.Bitmap);
//assign a delay rendered IStreamPersist implementation, here a VCL TBitmap
Clipboard.AssignVirtualFileDelayed<TBitmap>('Example.bmp', 
  procedure (BitmapToRender: TBitmap)
  begin
    BitmapToRender.SetSize({ ... });
	//... draw to BitmapToRender
  end);

Virtual file descriptors

On both Windows and OS X the file name passed to AssignVirtualFile or AssignVirtualFileDelayed will be used to name the 'real' file if and when the virtual one is pasted to (or dropped onto) the file system. On Windows this name will also be how the virtual file is identified before being pasted - in the terminology of CCR.Clipboard, it is the virtual file's 'descriptor'. On OS X however a still-virtual file is known only by its 'UTI' (Uniform Type Descriptor), which is the Apple way to describe data content. On copying, the TClipboard code for OS X will therefore derive a UTI from the extension of the file name passed:

var
  S: string;
begin
  Clipboard.AssignVirtualFile('Example.png', Image1.Bitmap);
  for S in Clipboard.GetVirtualFileDescriptors do
    ShowMessage(S);

Assuming no application has got in and changed the clipboard in between, the ShowMessage call here will show 'Example.png' on Windows but 'public.png' on OS X. For a list of standard UTIs, see Apple's documentation:

https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html

If you want to test for 'conformance' to a certain UTI, you can do so easily because TClipboardFormat on Apple platforms both prefers a UTI representation internally and provides built-in conformance testing. For example, to test whether a virtual file has a TIFF or TIFF-compatible format, then assuming OS X or iOS targeting, you can use the following syntax:

uses
  CCR.Clipboard.Apple;

function IsVirtualPngFile(const Descriptor: string): Boolean;
var
  Format: TClipboardFormat;
begin
  Format := TClipboardFormat.Wrap(Descriptor);
  Result := Format.ConformsTo(cfTIFF);
end;

Copying with attributes

Both AssignVirtualFile and AssignVirtualFileDelayed are overloaded with versions that allow specifying the timestamps and attributes of pasted files, together with their size:

var
  Details: TVirtualFileDetails;
begin
  Details.FileName := 'Example.dat';
  Details.Attr := faReadOnly; //use any valid faXXX combination
  Details.CreationDate := EncodeDate(2015, 02, 10);
  Details.LastAccessDate := Now;
  Details.LastWriteDate := EncodeDate(2015, 02, 11);
  Details.Size := 1024;
  Clipboard.AssignVirtualFileDelayed(Details, GetVirtualFileData);

This is really a Windows thing, however the timestamps and (of the attributes) faReadOnly will be respected on OS X too.

Pasting virtual file data

To paste vitual file data call either EnumVirtualFiles or SaveVirtualFiles. The first takes a callback method invoked for each virtual file available; this user-defined callback then receives the descriptor and a TClipboard-defined callback method for reading the data either to a stream or as TBytes:

Clipboard.EnumVirtualFiles(
  procedure (const Descriptor: string; 
    const SaveToStream: TProc<TStream>; var LookForMore: Boolean)
  var
    Stream: TMemoryStream;
  begin
    Stream := TMemoryStream.Create;
    try
      //...
    finally
      Stream.Free;
    end;
  end);

Clipboard.EnumVirtualFiles(
  procedure (const Descriptor: string; 
    const GetBytes: TFunc<TBytes>; var LookForMore: Boolean)
  var
    Bytes: TBytes;
  begin
    Bytes := GetBytes;
    //...
  end);

Virtual file data is only actually requested from the operating system when you call SaveToStream or GetBytes.

In contrast to EnumVirtualFiles, SaveVirtualFiles writes virtual file data to real files, optionally outputting the names of the pasted files to a TStrings object:

Clipboard.SaveVirtualFiles('C:\Dest\Dir\That\Exists', memPastedFileNames.Lines);

SaveVirtualFiles also has a callback version. Use this if you want to specify the destination directory on a case-by-case basis:

Clipboard.SaveVirtualFiles(
  procedure (const Descriptor: string; 
    const SaveToFile: TSaveVirtualFileFunc; var LookForMore: Boolean)
  var
    DestDir, DestFile: string;
  begin
    if not InputQuery('Paste Virtual File', 'Where do you want to paste "' +
      Descriptor + '" to?', DestDir) or (DestDir = '') then Exit;
    DestFile := SaveToFile(DestDir);
    ShowMessage('Pasted to ' + DestFile);
  end);