4

I’ve build a console application that has a command interpreter. To make things easier I needed to add support for reading the clipboard when ctrl+v is pressed. When I press ctrl+v I see the symbol ^V in the console, so I’m replacing that character with the clipboard text. After some googling I’ve found out that the clipboard can be accessed by System.Windows.Forms.Clipboard.GetText().

My question is: Is there a better solution to add clipboard support to a console application? Possibly without using System.Windows.Forms.Clipboard? Maybe an interop call can do the trick?

One of the drawbacks of this solution is that the clipboard only works when the thread is defined as [STAThread]. It would also be a lot nicer if I could get rid of the ^V symbol.

This is the code of the current solution:

using System;
using System.Threading;
using System.Windows.Forms;

namespace ConsoleApplication1
{
    class Program
    {
        public static readonly string ClipboardChar = Convert.ToChar(22).ToString();

        [STAThread]
        static void Main(string[] args)
        {
            Console.Write("Do some pastin': ");

            //read
            Console.ForegroundColor = ConsoleColor.White;
            string result = Console.ReadLine();
            Console.ResetColor();

            //read keyboard
            if (result.Contains(ClipboardChar))
            {
                result = result.Replace(ClipboardChar, Clipboard.GetText());
            }

            //write result
            Console.WriteLine("\nResult: ");
            Console.ForegroundColor = ConsoleColor.White; 
            Console.WriteLine(result);
            Console.ResetColor();

            Console.WriteLine("\nPress any key to continue...");
            Console.ReadKey();
        }
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Kees C. Bakker
  • 32,294
  • 27
  • 115
  • 203
  • Why... you don't want any reference to System.Windows.Forms? It's a standard .NET library, so your application won't get bigger when deployed. And if you use any other method, the startup of your application will be slower anyway – Martheen Jan 17 '11 at 10:24
  • 1
    How would it be slower? I've added some drawbacks of the current solution to the question: One of the drawbacks of this solution is that the clipboard only works when the thread is defined as [STAThread]. It would also be a lot nicer if I could get rid of the ^V symbol. – Kees C. Bakker Jan 17 '11 at 10:31
  • 1
    on different note, you may find these articles on extending console api by .NET useful: http://www.devsource.com/c/a/Using-VS/Console-Input-in-NET/, http://www.devsource.com/c/a/Using-VS/Working-with-Console-Screen-Buffers-in-NET/1/ – VinayC Jan 17 '11 at 10:32
  • @VinayC thanks man. Good to have some readin'! – Kees C. Bakker Jan 17 '11 at 10:38

2 Answers2

9

You can certainly use P/Invoke to do this. Please treat the sample code as proof-of-concept as it was rapidly cobbled together & tested. I've taken a few liberties - for instance my prototype for GlobalLock returns string although the Win API really returns LPVOID.

using System;
using System.Runtime.InteropServices;

namespace clipboard
{
    class Program
    {
        public static void Main(string[] args)
        {
            ConsoleKeyInfo ki = Console.ReadKey( true );
            if( ( ki.Key == ConsoleKey.V ) && ( ki.Modifiers == ConsoleModifiers.Control ) )
            {
                Console.WriteLine( "Ctrl+V pressed" );
                string s = ClipBoard.PasteTextFromClipboard();
                Console.WriteLine( s );
            }

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }

    class ClipBoard
    {
        [DllImport("user32.dll", SetLastError = true)]
        private static extern Int32 IsClipboardFormatAvailable( uint format );

        [DllImport("user32.dll", SetLastError = true)]
        private static extern Int32 OpenClipboard( IntPtr hWndNewOwner );

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr GetClipboardData( uint uFormat );

        [DllImport("user32.dll", SetLastError = true)]
        private static extern Int32 CloseClipboard();

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern Int32 GlobalLock( IntPtr hMem );

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern Int32 GlobalUnlock( IntPtr hMem );

        [DllImport("kernel32.dll")]
        public static extern UIntPtr GlobalSize(IntPtr hMem);

        const uint CF_TEXT = 1;

        public static string PasteTextFromClipboard()
        {
            string result = "";
            if( IsClipboardFormatAvailable( CF_TEXT ) == 0 )
            {
                return result; 
            }
            if( OpenClipboard((IntPtr)0) == 0 )
            {
                return result; 
            }

            IntPtr hglb = GetClipboardData(CF_TEXT);
            if( hglb != (IntPtr)0 )
            {
                UIntPtr size = GlobalSize(hglb);
                IntPtr s = GlobalLock(hglb);
                byte[] buffer = new byte[(int)size];
                Marshal.Copy(s, buffer, 0, (int)size);
                if (s != null)
                {
                    result = ASCIIEncoding.ASCII.GetString(buffer);
                    GlobalUnlock(hglb);
                }
            }

            CloseClipboard();
            return result;
        }
    }
}
Community
  • 1
  • 1
MikeJ-UK
  • 620
  • 3
  • 10
  • Looks good... but now I need to look into the question of how to simulate the ReadLine() with ReadKey() :D – Kees C. Bakker Jan 20 '11 at 12:47
  • This P/Invoke also gets rid of the [STAThread] that was needed for System.Windows.Forms.Clipboard! :D – Kees C. Bakker Jan 20 '11 at 17:31
  • I LOVE YOU Man. Very Smart and effective. I got rid of forms for efficieny and I really hated the idea of kinda injecting them again. Thanx – Mehdi LAMRANI Jan 09 '12 at 13:07
  • @Mika Jacobi - No problem. Actually the edit by Community has cleaned-up my original code so my comments about 'taking liberties' no longer apply! – MikeJ-UK Jan 11 '12 at 09:42
3

If you click the icon in the top left corner of the console application window you get a 'Edit' | 'Paste' option.

Peter
  • 41
  • 1