views:

93

answers:

1

I have a ThreadStateException in my WinForms application.

Step to reproduce:

  • Create simple winforms app
  • Add a Timer
  • In click event, do :

    timer1.Interval = 1000;
    timer1.Tick += timer1_Tick;
    timer1.Start();
    

    with

    void timer1_Tick(object sender, EventArgs e)
    {
        ThreadPool.QueueUserWorkItem(delegate
        {
            StringCollection paths =
                new StringCollection { @"c:\my.txt", @"c:\my.png" };
            Clipboard.SetFileDropList(paths);
        });
    }
    

The exception tells me:

Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.

But the main has already the [STAThread] attribute.

How to solve it?

A: 

The Thread.SetApartmentState() method is important here. The Clipboard is a COM object, it is not thread-safe. There are many Windows features that behave like this, Drag+Drop and the shell dialogs like OpenFileDialog are other examples.

You cannot set the apartment state of a threadpool thread, it is always MTA (Multi-threaded apartment). You can on a regular Thread but an additional STA requirement is that you also pump a message loop (Application.Run). Which gives you the exact same problem back: you cannot block or take a long time to execute code.

The simple solution is to do whatever takes a long time to execute on a thread. And make the Clipboard call from the UI thread. Use Control.BeginInvoke() or, better, BackgroundWorker.RunWorkerCompleted.

Hans Passant
arg, this will be an hard work ... I'm using a SynchronizationContext with this QueueUserWorkItem :(
Tim
Well, not correctly I'd guess. Use a BGW.
Hans Passant
with BeginInvoke and a BGW it's working. Thanks
Tim