Timer and Thread Issues

I am studying an example of a C # encoder which is not very advanced, so this problem completely surpassed me regardless of the amount of information on the Internet.

I essentially create a program that repeatedly polls a website on a timer to get some information. During this process, a WebBrowser control is created to navigate to the information (required for authentication). The program fires this series of events at startup, and then using System.Timers.Timer is set every 10 minutes (less for debugging, of course) to do the same sequence of events when my Timer.Elapsed event fires this process, I get a :

A ThreadStateException describing how an ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be created because the current thread is not in a single-threaded apartment.

Here is a small 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 have enough code to paint a picture. As you can see, when it comes to creating another WebBrowser, it throws an error.

I'm really dumb, and I'm just starting to scratch the threading surface, which is probably why I am so puzzled.

// Solution for me / I ended up moving the WebBrowser out of the method, and also making it static, just to reuse the WebBrowser control. I also changed my System.Timers.Timer to System.Threading.Timer. The problem seemed to be fixed.

+4
source share
3 answers

The MSDN documentation for WebBrowser states that:

The WebBrowser class can only be used in streams installed in single-threaded apartment mode (STA). To use this class, make sure that your main method is marked with the [STAThread] attribute .

Also, change System.Timers.Timer to System.Windows.Forms.Timer if you want to interact regularly with user interface controls. Alternatively, set the SynchronizingObject property of your System.Timers.Timer to the parent control to force the timer to invoke calls on the correct thread. All WinForms controls can only be accessed from the same user interface thread.

There are three types of timers in .NET BCL, each of which has a different effect. Check this MSDN article for comparison: Comparing timer classes in the .NET Framework class library (web archive) or a quick comparison table .

+7
source

I would recommend using a WebClient class instead of a WebBrowser. It also seems that it is better to store the already created instance as a private property, rather than creating a new instance every time you need to poll the website.

As below:

 private WebClient webClient; private void Form1_Load(object sender, EventArgs e) { GetDataFromWebBrowser(); Set_Auto_Refresh_Timer(); this.webClient = new WebClient(); } 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 Set_Auto_Refresh_Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { GetDataFromWebBrowser(); } private void GetDataFromWebBrowser() { ...perform required work with webClient... ...get web data... } 
+3
source

As Groi said, you should use System.Windows.Forms.Timer , or if you really want to do the operation in another thread, you should use the Invoke method to create any UI elements

 private void GetWebData() { ...get web data... } private void ShowWebData() { WebBrowser wb = new WebBrowser(); // other UI stuff } private void TimerRefresh_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { GetDataFromWebBrowser(); } private void GetDataFromWebBrowser() { GetWebData(); if (this.InvokeRequired) this.Invoke(new Action(ShowWebData)); else ShowWebData(); } 
0
source

Source: https://habr.com/ru/post/1393920/


All Articles