4

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

有谁知道为什么?

4

5 回答 5

3

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

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

于 2011-05-30T05:50:57.007 回答
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 的麻烦。

于 2011-05-30T05:50:03.697 回答
2

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

于 2011-05-30T14:49:16.907 回答
1

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

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

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 线程以外的线程上更改控件属性会导致问题时,它又回来了。对不起,如果这甚至不接近你正在尝试的东西。

于 2011-05-30T09:15:44.693 回答
0

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

于 2011-05-30T05:51:49.690 回答