Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble with timers and threads

I'm a learn-by-example C# coder who isn't very advanced, which is why this problem is completely stumping me regardless of the amount of information on the internet.

I'm essentially creating a program that is, on a timer, repeatedly polling a website to get some information. During this process, a WebBrowser control is created to navigate to the information (needed for authentication). The program runs this series of events at startup, then using a System.Timers.Timer set to every 10 minutes (less for debugging of course) to do that same series of events yet when my Timer.Elapsed event triggers that process, I get a:

ThreadStateException with the description as ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.

Here is a slimmed down version of my program.

private void Form1_Load(object sender, EventArgs e)
        {
            GetDataFromWebBrowser();
            Set_Auto_Refresh_Timer();
        }

private void Set_Auto_Refresh_Timer()
        {
            System.Timers.Timer TimerRefresh = new System.Timers.Timer(10000);
            TimerRefresh.Elapsed += new System.Timers.ElapsedEventHandler(TimerRefresh_Elapsed);
            TimerRefresh.AutoReset = true;
            TimerRefresh.Start();
        }    

private void TimerRefresh_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            GetDataFromWebBrowser();
        }

private void GetDataFromWebBrowser()
        {
            WebBrowser wb = new WebBrowser();  <--This is where the error is thrown.

            ...get web data...

        }

I think I got enough code in there to paint the picture. As you can see, when it gets to creating another WebBrowser, it throws the error.

I'm really stumped and I'm just starting to scrape the surface on Threading which is probably why I'm so stumped.

//Solution for me/ I ended up moving the WebBrowser creation out of the method as well as making it static to just reuse the WebBrowser control. I also swapped my System.Timers.Timer to System.Threading.Timer. Seemed to fix the problem.

like image 920
Josh Avatar asked Dec 27 '22 06:12

Josh


1 Answers

The MSDN documentation for WebBrowser states that:

The WebBrowser class can only be used in threads set to single thread apartment (STA) mode. To use this class, ensure that your Main method is marked with the [STAThread] attribute.

Also, change your System.Timers.Timer to a System.Windows.Forms.Timer if you want to interact with UI controls in regular intervals. Alternatively, set the SynchronizingObject property of your System.Timers.Timer to a parent control to force your timer to invoke calls on the right thread. All WinForms controls can only be accessed from the same, one and only UI thread.

There are three types of timers in .NET's BCL, each of them acting very differently. Check this MSDN article for a comparison: Comparing the Timer Classes in the .NET Framework Class Library (web archive) or this brief comparison table.

like image 74
Groo Avatar answered Jan 05 '23 00:01

Groo