每次剪贴板数据发生变化时,我都会尝试检测。因此,我设置了一个计时器,让它不断检查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)模式。
有谁知道为什么?
答案 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/