Reputation: 23253
I'm trying to detect every time the clipboard data changes. And so, I set a timer and have it continuously check Clipboard.GetText()
for changes.
I'm using the following code:
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();
}
I get the following error when it runs:
Current thread must be set to singlethread apartment (STA) mode before OLE calls can be made.
Does anyone know why?
Upvotes: 4
Views: 1898
Reputation: 15817
Polling the clipboard with a timer is very bad practice. The clipboard is a shared resource, and you'll be interfering with other apps that are monitoring the clipboard (via proper clipboard notification, i.e. following the rules). And you'll be colliding with whatever the user is trying to do. For example, when the user attempts to copy data to the clipboard, and you have opened it in your polling loop to examine it, he'll get "cannot open clipboard" errors or crashes. Please read up on clipboard viewers in MSDN: http://msdn.microsoft.com/en-us/library/ff468802(v=VS.85).aspx
Upvotes: 3
Reputation: 592
Not entirely sure with this but have you tried invoking the changing of text? I had the exact same error (although in a very different scenario) and invoking a method to change a control property solved it.
I created a delegate with a string parameter:
public delegate void TextBoxChangeDelegate(string text);
Then a method that will do the actual changing:
void TextBoxChange(string text)
{
MyTextBox.Text = text;
}
Then I invoke this method in my thread process (and in your case, the timer event):
public void ThreadService()
{
while(Running)
{
Invoke(new TextBoxChangeDelegate(TextBoxChange), new object[] { "New Value: "+ strNewValue });
}
}
This was in WinForms. It was back when I knew for the first time that changing control properties on a thread other than the UI thread causes problems. Sorry if this isn't even close to what you're trying.
Upvotes: 1
Reputation: 60987
It's the threading model of basically any managed Windows GUI application. The thread that is running the code ergo interfacing with the UI thread must be the one and the same. If you maintain the SynchronizationContext of your start up path some where (you can put this in a static varible) you can post messages to it. Those messages will end up executing on the correct thread and you'll not get this error.
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();
}
}
So, basically, when you are working with different threads (i.e. timers and what not) you need to remeber that the original start up thread is special (given that it's an STA thread). In order to invoke methods on objects that belong to that special thread you go through the SynchronizationContext which I've provided as a static member for my App class.
You might also wanna think about using a timer that actually dispatches to the main UI thread, then you wouldn't have to go trough the hassle of posting to the SynchronizationContext yourself.
Upvotes: 2
Reputation: 5604
Its because you can not use windows control in a thread delegate, basically timer is a thread and passing a delegate which using windows control is creating a problem. check if this helps http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/
Upvotes: 0
Reputation: 16757
Your method accesses the Clipboard class, which is an OLE call that needs the caller to be in STA mode. Most likely the issue here is with your timer, which operates on a different thread. Here is a link that will help you out with more information about this:
http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/
Also, here is a link to a full article on how to monitor the clipboard by tapping into the Windows events:
http://www.radsoftware.com.au/articles/clipboardmonitor.aspx
I think this article will give you some tips on how better to monitor the clipboard and thus you will avoid this issue. While it is still good to know why the error occurred, there is a better way for this task to be accomplished.
Upvotes: 3