【问题标题】:Waiting for clipboard text change (error)等待剪贴板文本更改(错误)
【发布时间】:2011-05-30 05:46:23
【问题描述】:

我试图检测每次剪贴板数据发生变化。因此,我设置了一个计时器并让它不断检查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) 模式。

有人知道为什么吗?

【问题讨论】:

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


    【解决方案1】:

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

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

    此外,这里是一篇完整文章的链接,该链接介绍了如何通过点击 Windows 事件来监控剪贴板:

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

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

    【讨论】:

      【解决方案2】:

      它基本上是所有托管 Windows GUI 应用程序的线程模型。运行与 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 线程)。为了在属于该特殊线程的对象上调用方法,您需要通过 SynchronizationContext,它是我作为 App 类的静态成员提供的。

      您可能还想考虑使用实际调度到主 UI 线程的计时器,这样您就不必自己处理发布到 SynchronizationContext 的麻烦。

      【讨论】:

      • 对不起 - 我对 C# 很陌生。有没有英文版的? :P
      • 我相信最好的沟通方式是通过代码。你知道该怎么做吗?
      【解决方案3】:

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

      【讨论】:

      • 嗯。谢谢指教。即使我只监视剪贴板(在上面的方法中)不到一秒钟,它仍然是真的吗?
      • 如果您正在监视剪贴板,您一定希望它上面会出现一些东西(否则您不会打扰)。您需要避免干扰将数据放入其中的任何内容。并且您需要考虑通过(合法的)剪贴板通知机制响应更新的其他应用程序。
      【解决方案4】:

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

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

      public delegate void TextBoxChangeDelegate(string text);
      

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

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

      然后我在我的线程进程中调用这个方法(在你的例子中,定时器事件):

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

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

      【讨论】:

        【解决方案5】:

        这是因为您不能在线程委托中使用 Windows 控件,基本上计时器是一个线程并传递使用 Windows 控件的委托会产生问题。 检查这是否有帮助 http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/

        【讨论】:

          猜你喜欢
          • 2012-02-04
          • 2021-06-21
          • 2019-01-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多