将字符串复制到剪贴板时出现“当前线程必须设置为单线程单元(STA)”错误

34

我尝试了来自如何在C#中复制数据到剪贴板的代码:

Clipboard.SetText("Test!");

我遇到了这个错误:

在进行 OLE 调用前,必须将当前线程设置为单线程公寓 (STA) 模式。请确保您的 Main 函数已标记为 STAThreadAttribute

如何修复它?


你是在尝试从后台线程调用这个方法(Clipboard.SetText())吗?你能给我们提供更多的上下文信息吗(例如,该函数调用周围的代码)? - Cody Gray
4个回答

65
如果您无法控制线程以STA模式运行与否(例如测试、插件到其他应用程序或仅仅是一些随机代码,将调用发送到非 UI 线程,您无法使用 Control.Invoke 将其发送回主 UI 线程),那么可以在专门配置为 STA 状态的线程上运行剪贴板访问,因为剪贴板访问需要 STA 状态(内部使用需要 STA 的 OLE)。
Thread thread = new Thread(() => Clipboard.SetText("Test!"));
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start(); 
thread.Join(); //Wait for the thread to end

这对于在Selenium测试中检查剪贴板内容非常有用,例如如果您有一个复制到剪贴板按钮。 - Nick Baker
1
这对我解决了错误。添加属性没有用。 - syonip
1
解决了这个问题,但为什么在主方法中有STAThread属性时它不允许我修改剪贴板? - Jamshaid K.
2
但它解决了我的问题。 - Muhammad Waqas Aziz
这解决了我的问题。我有一个来自SignalR事件的回调,需要将某些内容复制到剪贴板。 - Garrett Banuk

26

确保运行代码的线程标记有[STAThread]属性。对于WinForm和控制台应用程序,通常是Main方法。

在你的主方法上方放置[STAThread]

[STAThread]
static void Main()
{
}

对于WinForms,通常可以在生成的Main.cs文件中进行编辑(如果需要,它不会在更改时重新生成)。对于控制台应用程序,您定义Main的位置。

如果您无法控制线程(即您正在编写库或主应用程序由于某种原因被锁定),则可以在专门配置的线程上运行访问剪贴板的代码(.SetApartmentState(ApartmentState.STA)),如另一个答案所示。


我猜他正在使用WinForms,所以他无法访问“Main”。 - It'sNotALie.
@newStackExchangeInstance 我自己在WinForms中制作了一个剪贴板应用程序,所以我非常确定这对他有用 :) - Thousand
不是说它不能工作,只是说在WinForms上编辑“Main”很麻烦。 - It'sNotALie.
3
@new:怎么编辑?它在 Main.cs 文件中定义...非常容易编辑。主方法与是否使用 WinForms 无关。顺便提一句,新项目从一开始就正确设置了此属性。 - Cody Gray
1
@new 这是--Main.cs。它直接放在您项目的根目录中。要进行编辑,您只需双击即可。我没有看到任何麻烦之处。 - Cody Gray
显示剩余3条评论

17

你只能从STAThread访问剪贴板。

最快的解决方法是在Main()方法之上放置[STAThread],但如果由于任何原因你不能这样做,你可以使用一个单独的类来创建一个STAThread并为你设置/获取字符串值。

public static class Clipboard
{
    public static void SetText(string p_Text)
    {
        Thread STAThread = new Thread(
            delegate ()
            {
                // Use a fully qualified name for Clipboard otherwise it
                // will end up calling itself.
                System.Windows.Forms.Clipboard.SetText(p_Text);
            });
        STAThread.SetApartmentState(ApartmentState.STA);
        STAThread.Start();
        STAThread.Join();
    }
    public static string GetText()
    {
        string ReturnValue = string.Empty;
        Thread STAThread = new Thread(
            delegate ()
            {
                // Use a fully qualified name for Clipboard otherwise it
                // will end up calling itself.
                ReturnValue = System.Windows.Forms.Clipboard.GetText();
            });
        STAThread.SetApartmentState(ApartmentState.STA);
        STAThread.Start();
        STAThread.Join();

        return ReturnValue;
    }
}

0

这个问题已经存在8年了,但仍有很多人需要解决方案。

正如其他人所提到的,剪贴板必须从主线程或[STAThread]中调用。

好吧,我每次都使用这个解决方法。也许它可以作为一种替代方案。

public static void SetTheClipboard()
{
    Thread t = new Thread(() => {
        Clipboard.SetText("value in clipboard");
    });

    t.SetApartmentState(ApartmentState.STA);
    t.Start();
    t.Join();

    await Task.Run(() => {
        // use the clipboard here in another thread. Also can be used in another thread in another method.
    });
}

剪贴板值是在t线程中创建的。

关键是:线程t的公寓设置为STA状态。

稍后,您可以随意在其他线程中使用剪贴板值。

希望您能理解。


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