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

本文关键字:错误 在等待 剪贴板 文本 | 更新日期: 2023-09-27 17:57:52

我正在尝试检测剪贴板数据的每次更改。因此,我设置了一个计时器,让它不断地检查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();
}

当它运行时,我得到以下错误:

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

有人知道为什么吗?

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

您的方法访问剪贴板类,这是一个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

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

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

它基本上是任何托管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线程)。为了在属于该特殊线程的对象上调用方法,您需要经过SynchronizationContext,我为App类提供了它作为静态成员。

您可能还想考虑使用一个实际调度到主UI线程的计时器,这样您就不必自己费力地发布到SynchronizationContext。

对此不完全确定,但您是否尝试过调用文本更改?我有完全相同的错误(尽管在一个非常不同的场景中),调用一个方法来更改控制属性解决了这个问题

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

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线程以外的线程上更改控件属性会导致问题时,我就知道了。很抱歉,如果这甚至与你尝试的不接近。

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