如何在C#中将字符串内容复制到剪贴板?

305
如果我有一个字符串中的文本,如何将其复制到剪贴板中,以便用户可以将其粘贴到另一个窗口中呢(例如,从我的应用程序到记事本)?

5
不使用WinForms或WPF:参考链接:https://dev59.com/FmYr5IYBdhLWcg3wkbBM#13571530 - user359135
8个回答

395
您可以使用 System.Windows.Forms.Clipboard.SetText(...)

14
这是针对WinForms的;关于WPF,请参阅Jeff Moser的答案(目前排名第2位)。 - Danny Beckett
2
@DannyBeckett 现在只有 1 个到位。 - Anonymous Pi
6
对于控制台应用程序,您需要在 Main 函数上使用 [STAThread] 特性修饰符。 - John Henckel
问题说“一些文本”,但为了健壮性:如果字符串为空会发生什么?(参考链接:https://dev59.com/f2fWa4cB1Zd3GeqPfUIs) - Peter Mortensen

180

18
如果它是一个控制台应用程序,该怎么办? - Cheeso
16
您仍然可以使用任何一种方法,只需将winforms dll作为参考程序集引用即可。 - Jeff Moser
@Cheeso 针对控制台应用程序,请参见下面的答案:(https://dev59.com/ZnNA5IYBdhLWcg3wmfEa#899361) - xtreampb
在 .Net 4.6.1 控制台应用程序中,WPF 方法对我无效,但在将 [STAThread] 属性添加到 Main() 后,Winforms 方法完美地运行了。 - AceJordin
1
.NET 6(或5或Core)怎么样? - Patrick Szalapski

70

我希望调用 SetText 是很容易的,但事实上有很多需要注意的地方。你必须确保你在调用该方法的线程正在STA中运行。它有时会失败并出现访问被拒绝的错误,然后几秒钟后又可以正常工作 - 这与剪贴板中COM定时问题有关。如果你的应用程序通过远程桌面访问,访问剪贴板可能会有问题。我们使用一个集中的方法来处理所有这些情况,而不是直接调用SetText

@Stecy: 这是我们的集中式代码:

StaHelper类只是在单线程公寓(STA)中的线程上执行一些任意代码-该线程是剪贴板所必需的。

abstract class StaHelper
{
    readonly ManualResetEvent _complete = new ManualResetEvent( false );    

    public void Go()
    {
        var thread = new Thread( new ThreadStart( DoWork ) )
        {
            IsBackground = true,
        }
        thread.SetApartmentState( ApartmentState.STA );
        thread.Start();
    }

    // Thread entry method
    private void DoWork()
    {
        try
        {
            _complete.Reset();
            Work();
        }
        catch( Exception ex )
        {
            if( DontRetryWorkOnFailed )
                throw;
            else
            {
                try
                {
                    Thread.Sleep( 1000 );
                    Work();
                }
                catch
                {
                    // ex from first exception
                    LogAndShowMessage( ex );
                }
            }
        }
        finally
        {
            _complete.Set();
        }
    }

    public bool DontRetryWorkOnFailed{ get; set; }

    // Implemented in base class to do actual work.
    protected abstract void Work();
}

我们有一个特定的类来设置剪贴板上的文本。在一些Windows/.NET版本中的一些边缘情况下需要手动创建,我现在不记得确切的情况了,在.NET 3.5可能并不需要。

class SetClipboardHelper : StaHelper
{
    readonly string _format;
    readonly object _data;

    public SetClipboardHelper( string format, object data )
    {
        _format = format;
        _data = data;
    }

    protected override void Work()
    {
        var obj = new System.Windows.Forms.DataObject(
            _format,
            _data
        );

        Clipboard.SetDataObject( obj, true );
    }
}

使用方式如下:

new SetClipboardHelper( DataFormats.Text, "See, I'm on the clipboard" ).Go();

4
+1,我至少经历了其中的一些陷阱。如果我将剪贴板访问包装在try { ...} catch (System.Runtime.InteropServices.ExternalException) { }中,它对我来说运行得还不错。 - Joe
1
@Paul,你能否请解释一下你的集中式方法? - Stécy
3
这个回答应该获得更多的赞同票。另外,你应该创建一个自问自答的问题,比如“如何在非STA上下文中运行STA代码”。 - Sidney
2
在这里,一年半后,我几乎没有关于2016年2月19日的这个问题的记忆,再次复制这个类。 - Sidney
DataFormats是什么? - Kiquenet
我已将此问题关闭为 https://dev59.com/4XA65IYBdhLWcg3w8zjt#34077334 的重复。考虑将您的答案移至那里(如果有兴趣,同时看看是否可以现代化为 Task/async/await)。 - Alexei Levenkov

28

WPF: System.Windows.Clipboard (PresentationCore.dll)

Winforms: System.Windows.Forms.Clipboard

两者都有静态的SetText方法。


感谢您包含PresentationCore.dll参考。在控制台应用程序中默认不包含此参考。 - xtreampb

18

这对我有效:

你想要做的是:

System.Windows.Forms.Clipboard.SetText("String to be copied to Clipboard");

但是它会出现一个错误,说它必须在单线程ApartmentState.STA中运行。

因此,让我们将其运行在这样的线程中。其代码如下:

public void somethingToRunInThread()
{
    System.Windows.Forms.Clipboard.SetText("String to be copied to Clipboard");
}

protected void copy_to_clipboard()
{
    Thread clipboardThread = new Thread(somethingToRunInThread);
    clipboardThread.SetApartmentState(ApartmentState.STA);
    clipboardThread.IsBackground = false;
    clipboardThread.Start();
}

调用 copy_to_clipboard() 后,字符串将被复制到剪贴板中,因此您可以粘贴或使用 Ctrl + V 将其粘贴回来,获取字符串作为要复制到剪贴板的字符串


一针见血。谢谢! - CodeBreaker

15

使用这个问题中展示的解决方案,System.Windows.Forms.Clipboard.SetText(...)会导致以下异常:

在进行OLE调用之前,当前线程必须设置为单线程公寓(STA)模式

为了避免这种情况,你可以添加以下属性:

[STAThread]

为了

static void Main(string[] args)

8

在Windows Forms中,如果您的字符串在文本框中,则可以轻松使用以下方法:

textBoxcsharp.SelectAll();
textBoxcsharp.Copy();
textBoxcsharp.DeselectAll();

2
textBox1.Copy(); - Dozer789
4
实际上,你需要使用SelectAll()和DeselectAll()函数。根据https://msdn.microsoft.com/en-us/library/system.windows.forms.textboxbase.copy(v=vs.120).aspx所述,复制到剪贴板的是**当前选定**的文本。 - G. Stewart
3
如果不使用SelectAll(),它就不能正常工作,我刚在这个无用的应用程序中尝试了一下。在WPF应用程序中没有必要使用DeselectAll()。 - Magnetic_dud
我喜欢这个功能,因为它不需要在类中添加STA属性。我只是想在调试期间使用它,并且不想改变代码的其他部分,只是为了将某些内容放入键盘缓冲区。 - MattSlay

-2

即使有错误,也要使用try-catch,它仍然会复制。

Try
   Clipboard.SetText("copy me to clipboard")
Catch ex As Exception

End Try

如果您使用消息框来捕获异常,它会显示错误,但是该值仍将复制到剪贴板。

你确定吗?你尝试过多次,例如10次吗?也许你只是幸运的?通常需要重试(作为解决方法)吗? - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接