Friday, September 26, 2008

Determining If a File Is Cut or Copied to the Clipboard

I received a question on the Microsoft Forums today regarding whether or not a file is cut or copied to the clipboard. Using the System.Windows.Forms.Clipboard class, we can retrieve the data that is on the Windows Clipboard, however, only a small subset of this information is exposed easily through the managed methods on the clipboard. Enter the IDataObject interface.

You can access an object implementing this interface from the Clipboard.GetData() static method. The resulting implementation can return you a list of Formats included in the clipboard. Using these format strings, you can retrieve all of the information on the Clipboard. For example, take the following code:

   1:  IDataObject d = Clipboard.GetDataObject();

   2:  foreach (string format in d.GetFormats())

   3:      Console.WriteLine("Format: {0},\n .NET Data Type {1}\n\n", format, d.GetData(format).GetType().FullName);

   4:  Console.ReadLine();

Right clicking on a file in Explorer and copying a file, and then running the code above gives me the following output in the console:

Format: Shell IDList Array,
.NET Data Type System.IO.MemoryStream

Format: FileDrop,
.NET Data Type System.String[]

Format: FileNameW,
.NET Data Type System.String[]

Format: FileName,
.NET Data Type System.String[]

Format: Preferred DropEffect,
.NET Data Type System.IO.MemoryStream

Format: Shell Object Offsets,
.NET Data Type System.IO.MemoryStream

Playing around with these values can give you some interesting results. For instance, the format "Preferred DropEffect" is actually a MemoryStream mapped to an integer value. Converting it from a MemoryStream to an Int32, you can determine if a file was cut or copied to the clipboard in explorer:

   1:  static DragDropEffects GetCurrentDropEffect()

   2:  {

   3:      string format = "Preferred DropEffect";


   5:      Object obj = Clipboard.GetData(format);


   7:      if (obj != null)

   8:      {

   9:          MemoryStream ms = (MemoryStream)obj;

  10:          BinaryReader br = new BinaryReader(ms);

  11:          return (DragDropEffects)br.ReadInt32();

  12:      }

  13:      return DragDropEffects.None;

  14:  }

Check it out for yourself. Here's a little console app to display the results:

   1:  [STAThread]

   2:  static void Main(string[] args)

   3:  {

   4:      Console.WriteLine("Perform a file operation.");

   5:      do

   6:      {

   7:          Console.Clear();

   8:          DragDropEffects currentEffect = GetCurrentDropEffect();

   9:          string cutOrCopy = "None";

  10:          switch (currentEffect)

  11:          {

  12:              case DragDropEffects.Copy | DragDropEffects.Link:

  13:                  cutOrCopy = "Copy";

  14:                  break;

  15:              case DragDropEffects.Move:

  16:                  cutOrCopy = "Cut";

  17:                  break;

  18:          }

  19:          Console.WriteLine("Last operation was: {0}", cutOrCopy);

  20:          Console.WriteLine("Perform a function and press enter or type \"exit\" to quit");

  21:      } while (Console.ReadLine() != "exit");

  22:  }

That's all I got for today!