问题描述
|
我正尝试检测每次剪贴板数据更改。因此,我设置了一个计时器并使它连续检查
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调用。
有人知道为什么吗?
解决方法
它几乎是任何托管Windows GUI应用程序的线程模型。运行与UI线程接口的代码ergo的线程必须相同。如果您将启动路径的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了。
, 您的方法访问Clipboard类,这是一个OLE调用,需要调用方处于STA模式。这很可能是您的计时器出现问题,该计时器在其他线程上运行。这是一个链接,可以帮助您了解有关此的更多信息:
http://social.msdn.microsoft.com/Forums/zh-CN/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/
另外,这里是指向完整文章的链接,该文章介绍了如何通过利用Windows事件来监视剪贴板:
http://www.radsoftware.com.au/articles/clipboardmonitor.aspx
我认为本文将为您提供一些有关如何更好地监视剪贴板的提示,从而可以避免此问题。虽然知道为什么会发生错误仍然很不错,但是有一种更好的方法可以完成此任务。
, 对此不完全确定,但是您是否尝试调用文本更改?我遇到了完全相同的错误(尽管在非常不同的情况下),并且调用一种更改控件属性的方法解决了它。
我创建了一个带有字符串参数的委托:
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线程以外的线程上的控件属性会导致问题时,就回到了当初。很抱歉,如果这个距离与您尝试的距离还很近。
, 用计时器轮询剪贴板是非常不好的做法。剪贴板是共享资源,您将干扰其他正在监视剪贴板的应用(通过适当的剪贴板通知,即遵循规则)。而且您将与用户尝试执行的任何操作发生冲突。例如,当用户尝试将数据复制到剪贴板时,如果您已在轮询循环中将其打开以进行检查,则他会收到“无法打开剪贴板”错误或崩溃。请在MSDN中的剪贴板查看器上阅读:http://msdn.microsoft.com/zh-cn/library/ff468802(v=VS.85).aspx
, 这是因为您不能在线程委托中使用Windows控制,基本上计时器是一个线程,并且传递一个使用Windows控制的委托会导致问题。
检查这是否有帮助
http://social.msdn.microsoft.com/Forums/zh-CN/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/