等待剪贴板文本更改(错误)

时间:2011-05-30 05:46:24

标签: c# .net exception-handling clipboard sta

每次剪贴板数据发生变化时,我都会尝试检测。因此,我设置了一个计时器,让它不断检查Clipboard.GetText()是否有变化。

我正在使用以下代码:

public void WaitForNewClipboardData()
{
    //This is in WPF, Timer comes from System.Timers
    Timer timer = new Timer(100);
    timer.Elapsed += new ElapsedEventHandler(
        delegate(object a, ElapsedEventArgs b){
            if (Clipboard.GetText() != ClipBoardData)
            {
                SelectedText.Text = Clipboard.GetText();
                ClipBoardData = Clipboard.GetText();
                timer.Stop();
            }
        });
    timer.Start();
}

运行时出现以下错误:

  

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

有谁知道为什么?

5 个答案:

答案 0 :(得分:3)

您的方法访问Clipboard类,这是一个需要调用者处于STA模式的OLE调用。最有可能的问题是你的计时器,它在不同的线程上运行。这是一个链接,可以帮助您了解更多相关信息:

http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/

另外,这里有一篇关于如何通过点击Windows事件来监控剪贴板的完整文章的链接:

http://www.radsoftware.com.au/articles/clipboardmonitor.aspx

我认为本文将为您提供有关如何更好地监控剪贴板的一些提示,从而避免此问题。虽然知道错误发生的原因仍然很好,但是有更好的方法可以完成这项任务。

答案 1 :(得分:2)

这是基本上任何托管Windows GUI应用程序的线程模型。运行代码ergo与UI线程连接的线程必须是同一个。如果您将启动路径的SynchronizationContext维护在某个位置(您可以将其置于静态变量中),则可以向其发布消息。这些消息将最终在正确的线程上执行,您将不会收到此错误。

public partial class App : Application
{
    public static SynchronizationContext SynchronizationContext;

    protected override void OnStartup(StartupEventArgs e)
    {
        // This is my preferred way of accessing the correct SynchronizationContext in a WPF app
        SynchronizationContext = SynchronizationContext.Current;

        base.OnStartup(e);

        var mainWindow = MainWindow;

        var t = new Thread(() => {
            Thread.Sleep(3000);

            SynchronizationContext.Post(state => {
                mainWindow.Hide(); // this will not throw an exception
            }, null);

            mainWindow.Close(); // this will throw an exception
        });

        t.Start();
    }
}

所以,基本上,当你使用不同的线程(即定时器和什么不是)时,你需要记住原始的启动线程是特殊的(假设它是一个STA线程)。为了在属于该特殊线程的对象上调用方法,您将通过我作为App类的静态成员提供的SynchronizationContext。

您可能还想考虑使用实际调度到主UI线程的计时器,然后您不必为自己发布到SynchronizationContext而烦恼。

答案 2 :(得分:1)

对此不完全确定,但您是否尝试过调用文本的更改?我有完全相同的错误(虽然在一个非常不同的情况下)并调用一个方法来更改控件属性解​​决了它。

我创建了一个带有字符串参数的委托:

public delegate void TextBoxChangeDelegate(string text);

然后是一个将进行实际更改的方法:

void TextBoxChange(string text)
{
   MyTextBox.Text = text;
}

然后我在我的线程进程中调用此方法(在您的情况下,调用timer事件):

public void ThreadService()
{
  while(Running)
  {
    Invoke(new TextBoxChangeDelegate(TextBoxChange), new object[] { "New Value: "+ strNewValue });
  }

}

这是在WinForms中。当我第一次知道在UI线程之外的线程上更改控件属性导致问题时,它又回来了。对不起,如果这与您正在尝试的内容差不多。

答案 3 :(得分:1)

使用计时器轮询剪贴板是非常糟糕的做法。剪贴板是共享资源,您将干扰正在监视剪贴板的其他应用程序(通过正确的剪贴板通知,即遵循规则)。而且你将与用户试图做的任何事情发生冲突。例如,当用户尝试将数据复制到剪贴板,并且您已在轮询循环中打开它以检查它时,他将“无法打开剪贴板”错误或崩溃。请阅读MSDN中的剪贴板查看器:http://msdn.microsoft.com/en-us/library/ff468802(v=VS.85).aspx

答案 4 :(得分:0)

因为你不能在线程委托中使用windows控件,基本上timer是一个线程并且传递一个使用windows控件的委托创建了一个问题。 检查这是否有帮助 http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/