31

I have a .NET 4.0 console application that generates SQL and stores it in a string variable. I want this string to be copied directly to the clipboard.

So far, all my research indicates that the ONLY way this can possibly be done is by adding a reference to System.Windows.Forms. I do not want to add a reference to an assembly that is irrelevant to a console application.

Within the universe in which we currently exist, is there a known method of copying a string of text to the clipboard within a console application that does not involve adding a reference to System.Windows.Forms nor any other assembly whose purpose is irrelevant to a bare-bones console application?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
oscilatingcretin
  • 10,457
  • 39
  • 119
  • 206

2 Answers2

53

Platform invoking the clipboard APIs is a possible solution. Example:

using System.Runtime.InteropServices;
class Program
{
    [DllImport("user32.dll")]
    internal static extern bool OpenClipboard(IntPtr hWndNewOwner);

    [DllImport("user32.dll")]
    internal static extern bool CloseClipboard();

    [DllImport("user32.dll")]
    internal static extern bool SetClipboardData(uint uFormat, IntPtr data);

    [STAThread]
    static void Main(string[] args)
    {
        OpenClipboard(IntPtr.Zero);
        var yourString = "Hello World!";
        var ptr = Marshal.StringToHGlobalUni(yourString);
        SetClipboardData(13, ptr);
        CloseClipboard();
        Marshal.FreeHGlobal(ptr);
    }
}

This is just an example. Adding a little error handling around the code, like checking the return values of the P/Invoke functions, would be a good addition.

SetClipboardData is the interesting bit. You also want to make sure you open and close the clipboard, too.

The 13 passed in as the first argument is the data format. 13 means a Unicode string.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vcsjones
  • 138,677
  • 31
  • 291
  • 286
  • Sweet! It works. I will add that I needed to import the System.Runtime.InteropServices namespace. Other than that, it works! – oscilatingcretin Nov 26 '12 at 19:38
  • Thanks! vcsjones, actually, this method crushes sometimes (I really don't know the reason). The answer bellow with the class Clippy fixes the crushes in my app. Thanks anyway – Ilya Schukin Aug 26 '14 at 18:31
  • Besides the `Marshal.StringToHGlobalUni` [problem](http://stackoverflow.com/q/26054040/33499), I read on MSDN "_If SetClipboardData succeeds, the system owns the object identified by the hMem parameter. The application may not write to or free the data_". – wimh Mar 06 '16 at 10:48
9

The Marshal.StringToHGlobalUni function actually allocates memory in a fashion unsuitable for SetClipboardData (using LocalAlloc with LMEM_FIXED), which can cause crashes (you wouldn't expect it given the method name, but stepping into the code e.g. using ReSharper reveals this).

SetClipboardData requires GlobalAlloc with GMEM_MOVABLE according to the documentation: SetClipboardData on MSDN.

Here's an MIT licensed System.Windows.Forms alternative, tested and complete with error handling: Clippy

(The clipboard pushing code itself is to be found here: Clippy.cs.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David
  • 261
  • 4
  • 4