Как скопировать строку в буфер обмена в моем консольном приложении БЕЗ добавления ссылки на System.Windows.Forms?

У меня есть консольное приложение .NET 4.0, которое генерирует SQL и сохраняет его в строковой переменной. Я хочу, чтобы эта строка была скопирована непосредственно в буфер обмена.

До сих пор все мои исследования показывают, что ТОЛЬКО это возможно сделать, добавив ссылку на System.Windows.Forms. Я не хочу добавлять ссылку на сборку, которая не имеет отношения к консольному приложению.

В пределах юниверса, в котором мы в настоящее время существуем, существует ли известный способ копирования строки текста в буфер обмена в консольном приложении, который не включает добавление ссылки на System.Windows.Forms или любую другую сборку, целью которой является не имеет отношения к консольному приложению bare-bones?

Ответ 1

Платформа, вызывающая, API-интерфейс буфера обмена является возможным решением. Пример:

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);
    }
}

Это просто пример. Добавление небольшой обработки ошибок вокруг кода, например проверка возвращаемых значений функций P/Invoke, будет хорошим дополнением.

SetClipboardData - интересный бит. Вы также хотите, чтобы вы открыли и закрывали буфер обмена.

13 передается как первый аргумент - это формат данных. 13 означает строку Unicode.

Ответ 2

Функция Marshal.StringToHGlobalUni фактически распределяет память в непригодном для SetClipboardData (using LocalAlloc with LMEM_FIXED) способе, который может привести к сбоям (вы не ожидали, что это дано имя метода, но вступает в код, например, используя ReSharper показывает это).

SetClipboardData требует GlobalAlloc с GMEM_MOVABLE в соответствии с документацией: SetClipboardData на MSDN.

Здесь MIT лицензировал System.Windows.Forms альтернативу, протестировал и завершил обработку ошибок: Clippy

(Сам код нажатия на буфер обмена можно найти здесь: Clippy.cs.)