I have a third party library containing a class which performs a function asynchronously. The class inherits from the Form. The function basically performs a calculation based on data stored in a database. Once it has finished, it calls a _Complete event in the calling form.
What I would like to do is call the function synchronously but from a non-windows form application. The problem is, no matter what I do, my application blocks and the _Complete event handler never fires. From a windows form I can simulate the function running synchronously by using a "complete" flag and a "while (!complete) application.doevents", but obviously application.doevents isnt available in a non-windows form application.
Is there something that would stop me using the class's method outside of a windows form application (due to it inheriting from 'Form') ?
Is there some way I can work around this ?
Thanks,
Mike
At a stab it might be worth trying something like the following which uses a WaitHandle to block the current thread rather than spinning and checking a flag.
using System;
using System.Threading;
class Program
{
AutoResetEvent _autoEvent;
static void Main()
{
Program p = new Program();
p.RunWidget();
}
public Program()
{
_autoEvent = new AutoResetEvent(false);
}
public void RunWidget()
{
ThirdParty widget = new ThirdParty();
widget.Completed += new EventHandler(this.Widget_Completed);
widget.DoWork();
// Waits for signal that work is done
_autoEvent.WaitOne();
}
// Assumes that some kind of args are passed by the event
public void Widget_Completed(object sender, EventArgs e)
{
_autoEvent.Set();
}
}
I've got some more information on this problem (I'm working in the same team as mikecamimo).
The problem also occurs in the Windows Forms application, when replicated correctly. In the original OP, the problem didn't occur in the windows form because there was no blocking. When blocking is introduced by using a ResetEvent, the same problem occurs.
This is because the event handler (Widget_Completed) is on the same thread as the method calling Widget.DoWork. The result that AutoResetEvent.WaitOne(); blocks forever because the event handler is never called to Set the event.
In a windows forms environment this can worked around by using Application.DoEvents to poll the message queue and allow the event the be handled. See below.
using System;
using System.Threading;
using System.Windows.Forms;
class Program
{
EventArgs data;
static void Main()
{
Program p = new Program();
p.RunWidget();
}
public Program()
{
_autoEvent = new AutoResetEvent(false);
}
public void RunWidget()
{
ThirdParty widget = new ThirdParty();
widget.Completed += new EventHandler(this.Widget_Completed);
data = null;
widget.DoWork();
while (data == null);
Application.DoEvents();
// do stuff with the results of DoWork that are contained in EventArgs.
}
// Assumes that some kind of args are passed by the event
public void Widget_Completed(object sender, EventArgs e)
{
data = e;
}
}
In a non windows forms application, such as a Windows Service, Application is not available so DoEvents cannot be called.
The problem is one of threading and that widget.DoWork's associated event handler somehow needs to be on another thread. This should prevent AutoResetEvent.WaitOne from blocking indefinitely. I think... :)
Any ideas on how to accomplish this would be fantastic.
AutoResetEvent _autoEvent = new AutoResetEvent(false);
public WebBrowser SyncronNavigation(string url)
{
WebBrowser wb = null;
wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.ScriptErrorsSuppressed = true;
wb.Navigate(new Uri(url));
while (!_autoEvent.WaitOne(100))
Application.DoEvents();
return wb;
}
void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
//throw new NotImplementedException();
_autoEvent.Set();
}
Do you have the source for the component? It sounds like it's relying on the fact it will be called from a WinForms environment (must be a good reason why a library inherits from Form!), but it's hard to know for sure.
Related
This is an incredibly simple task tray app - using ApplicationContext and a few guides I found online.
The purpose of the app is to query a small REST API and show a message box to the user on a given result. I need to essentially have the API query in a background loop, running every 10 seconds or something similar. This is to report on data that I've made accessible via another service.
I've done some reading and it seems a BackgroundWorker and Timer is an appropriate option, but I'm lost on where to go next. How exactly can I achieve this? I initially tried adding a while(true) loop to the TaskTrayApplicationContext but it just created an infinite loop whereby you couldn't do anything else with the app.
namespace TaskTrayApplication
{
public class TaskTrayApplicationContext : ApplicationContext
{
NotifyIcon notifyIcon = new NotifyIcon();
Configuration configWindow = new Configuration();
public TaskTrayApplicationContext()
{
MenuItem configMenuItem = new MenuItem("Configuration", new EventHandler(ShowConfig));
MenuItem exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
notifyIcon.Icon = TaskTrayApplication.Properties.Resources.AppIcon;
notifyIcon.DoubleClick += new EventHandler(ShowMessage);
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { configMenuItem, exitMenuItem });
notifyIcon.Visible = true;
}
void ShowMessage(object sender, EventArgs e)
{
// Only show the message if the settings say we can.
if (TaskTrayApplication.Properties.Settings.Default.ShowMessage)
MessageBox.Show("This is the Serenity TaskTray Agent.");
}
void ShowConfig(object sender, EventArgs e)
{
// If we are already showing the window meerly focus it.
if (configWindow.Visible)
configWindow.Focus();
else
configWindow.ShowDialog();
}
void Exit(object sender, EventArgs e)
{
// We must manually tidy up and remove the icon before we exit.
// Otherwise it will be left behind until the user mouses over.
notifyIcon.Visible = false;
Application.Exit();
}
}
}
And the Program.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TaskTrayApplication
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Instead of running a form, we run an ApplicationContext.
Application.Run(new TaskTrayApplicationContext());
}
}
}
Threading is hard, concurrency is hard. Background worker and System.Timers are both constructs that run in their own thread.
winforms won't allow for interaction between threads that own a control (read: that created a control) and threads that don't. This is a whole subject apart i wont get into now - theres good stuff to read out there why this is and how to go about it: https://visualstudiomagazine.com/articles/2010/11/18/multithreading-in-winforms.aspx
There are tools to help, one is the dispatchertimer:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netcore-3.1
This is a special timer that instead of its own thread, schedules tasks on the main thread. The main thread in a winforms application handles the drawing of controls, showing of the different windows etc. e.g. this 'owns' all controls.
A sample can be seen on msdn, i adopted it here to show you what you could do:
public class TaskTrayApplicationContext : ApplicationContext
{
...
DispatcherTimer dispatcherTimer;
public TaskTrayApplicationContext()
{
...
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// Fetch your data via a rest api
var myData = MyDataFunction();
// check and show dialog if the data is not okay
if(myData.Result.Value = 'NOT_OKAY!')
ShowMessage(this, myData.Result); // or something.
}
...
Now since this does not utilize a second thread, this means the main ui thread could be blocked from drawing the windows, reacting to user input etc. because its busy doing work in the timer_tick function. This would for example happen if your rest call takes a long time.
This will make your application freeze and irresponsive. This could be a problem but most likely wont, so lets burn that bridge when we get to it.
Usually, when you access controls in a Thread you end up with some cross thread exceptions. In my C# WinForms Application I have a picture box and a toolstriplabel which do not cause that exception. I don't understand why, can anybody explain this to me?
Here some code explanation:
In the main form I have a picturebox and a toolstriplabel. Also I have a reference to another Form, which has no controls and no additional source code. And then in the main form there is another object which works with a thread. This thread can raise three different events and the main form is subscribed to these three events.
Event1 causes the toolstriplabel to update (with some information from the thread).
Event2 causes the picturebox to update (with a new picture from the thread).
Event1 and Event2 work perfectly fine. I do not use any invoke methods, I directly change Text and BackgroundImage properties without cross thread exception.
Event3 though makes troubles. It is supposed to show the other form but I receive the cross therad exception. It works only if I use a BeginInvoke to show the form.
Why is that?
Edit:
The multithreading is done by an MJPEGStream object. I subscribe the NewFrame method of that MJPEGStream object.
public partial class Form1 : Form
{
private CAM cam;
private PeekWindow frmPeekWindow;
public Form1()
{
InitializeComponent();
cam = new CAM();
cam.NewImageMessageEvent += new NewImageEventHandler(cam_NewImageMessageEvent);
cam.DetectionEvent += new DetectionEventHandler(cam_DetectionEvent);
cam.FpsChangedMessageEvent += new FpsChangedEventHandler(cam_FpsChangedMessageEvent);
cam.DetectionThreshold = (float)this.numDetectionThreshold.Value;
frmPeekWindow = new PeekWindow();
// without the next two lines, frmPeekwindow.Show() won't work if called in an event
frmPeekWindow.Show();
frmPeekWindow.Hide();
}
void cam_FpsChangedMessageEvent(object sender, FpsChangedEventArgs e)
{
lblFPS.Text = string.Format("fps: {0:0.0}", e.FPS);
}
void cam_DetectionEvent(object sender, DetectionEventArgs e)
{
if (chkEnablePeakWindow.Checked)
{
if (frmPeekWindow.InvokeRequired)
{
frmPeekWindow.Invoke((MethodInvoker)delegate()
{
frmPeekWindow.Show();
frmPeekWindow.setImage(e.Image);
});
}
else
{
frmPeekWindow.Show();
frmPeekWindow.setImage(e.Image);
}
}
}
void cam_NewImageMessageEvent(object sender, NewImageEventArgs e)
{
picStream.BackgroundImage = e.Image;
}
}
And here's the CAM class:
class CAM
{
private object lockScale = new object();
private MJPEGStream stream;
private Bitmap image;
public event NewImageEventHandler NewImageMessageEvent;
public event FpsChangedEventHandler FpsChangedMessageEvent;
public event DetectionEventHandler DetectionEvent;
// configure (login, pwd, source)
public CAM()
{
this.stream = new MJPEGStream("...");
this.stream.Login = "...";
this.stream.Password = "...";
this.stream.NewFrame += new NewFrameEventHandler(OnNewFrame)
}
private void OnNewFrame(object sender, NewFrameEventArgs ev)
{
try
{
FpsChangedMessageEvent(this, new FpsChangedEventArgs(10));
// get image
image = ev.Frame;
NewImageMessageEvent(this, new NewImageEventArgs(new Bitmap(image)));
DetectionEvent(this, new DetectionEventArgs(new Bitmap(image)));
}
catch (Exception ex)
{
Console.Out.WriteLine(ex.Message);
}
}
}
You won't get cross thread exception, but it doesn't mean that this is a safe operation. There is always a possibility for your control to go unstable. You just don't know when it will happen.
See the following explanation from Microsoft.
http://msdn.microsoft.com/en-us/library/ms171728.aspx
Access to Windows Forms controls is not inherently thread safe. If you
have two or more threads manipulating the state of a control, it is
possible to force the control into an inconsistent state. Other
thread-related bugs are possible, such as race conditions and
deadlocks. It is important to make sure that access to your controls
is performed in a thread-safe way.
I have these three possibilites in mind:
The action is already dispatched to the gui thread.
The action doesn't need to be dispatched currently.
The action is somehow executed from the gui thread.
It's most likely number 3.
You don't necessarily always have to call BeginInvoke/Invoke. Sometimes the operation is running on the foreground thread, sometimes it is in the background.
Per the microsoft samples that are everywhere, You can SHOULD check to see if calling BeginInvoke/Invoke is required.
private void SetTextStandardPattern()
{
if (this.InvokeRequired)
{
this.Invoke(SetTextStandardPattern);
return;
}
this.text = "New Text";
}
Here is a nice microsoft article that has a sample:
http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
and here is another article on how to "avoid" the pattern:
http://www.codeproject.com/Articles/37642/Avoiding-InvokeRequired
I have a problem and don't know where it is...
if I do this in a windows Form application:
private void btnListen_Click(...)
{
var t = new Thread(DoWork);
t.SetAppartmentState(ApartmentState.STA);
t.Start();
}
the DoWork method...
private void DoWork()
{
var controls = GetControls();
foreach (var c in controls)
{
control.OnEvent += HandleEvent;
}
}
private void HandleEvent()
{
DoSomething...
}
The controls are Wrapped Com objects registered with regsrv32
When firing events on controls, I don't recive events in my Windows forms application HandleEvent method.
but if I change the code to this:
private void btnListen_Click(...)
{
DoWork();
}
the it works all ok.
I need this to be executed in another thread.
Why isn't this working when using threads?
Please help.
thanks
EDIT:
I also tried this in a windows service.
Like this:
OnStart()
{
Task t = new Task(()=>DoWork);
t.Start();
}
every thing else is the same and still does not work. And there is no UI thread here.
You can create a thread in HandleEvent that does what you need it to do. You have to synchronize any thread you create with the UI thread if you need it to interact with the UI.
the problem was the control driver (COM) dll that didn't work. Installed new version and now it works.
im new to C# Event and i want to fire an event without getting a Cross-Thread error..
using System;
using System.Timers;
public class SampleTickEvent
{
private string passStr = string.Empty;
Timer t = new Timer(1000);
public delegate void ImageEventHandler(string s);
public event ImageEventHandler ImageEventTrigger;
public void Start(string ss)
{
passStr = ss;
t.Start();
t.Elapsed += t_Elapsed;
}
public void t_Elapsed(object sender, ElapsedEventArgs eea)
{
ImageEventTrigger(passStr);
}
}
private void button1_Click(object sender, EventArgs e)
{
SampleTickEvent ste = new SampleTickEvent();
ste.Start("sample");
ste.ImageEventTrigger += ste_ImageEventTrigger;
}
private void ste_ImageEventTrigger(string s)
{
Action act = () => listBox1.Items.Add(s);
Invoke(act);
}
is there another way that i will not put the Action act = () = ... and put listbox1.Items.Add(s) instead?
Rather than using System.Timers.Timer, trying using System.Windows.Forms.Timer, which is written so that it raises the event on the UI thread.
If you're in Windows Forms, you can use System.Windows.Forms.Timer instead of System.Timers.Timer. The tick event executes in the UI Thread : http://netpl.blogspot.com/2010/05/systemwindowsformstimer-vs.html
No, Timers operate on a thread from the thread pool, and so to safely modify your form controls, you need to use Invoke.
You could inline the action, but that's about it.
If you want to put the Invoke into the SampleTickEvent.t_Elapsed method, then you'll have to pass a Control as a handle into the SampleTickEvent first. Or, you could just create a new Control as a member of the the SampleTickEvent in its constructor, and call Invoke on that. I've done it that way before.
You could change this option during application startup:
System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
This would go in whichever Form you're using as the main application thread.
Try using the System.Windows.Forms.Timer instead of System.Timers.Timer.
From above Msdn Link:
This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.
I'm doing an application that does some sort of scanning (it checks availability of URL's through a short list) and depending on the result, it adds to one or another listbox. if it exists, it goes to lstOK, else, it goes to lst404.
The issue is that these web checks take time (specially when it is OK), it takes an awfully long time, and inserts all the items in the listboxes in the end, while the form is "not responding" and nothing appears or can be clicked or displays any interaction.
Is there a way for the form to be still usable and the listboxes to update on the go ?
This should be simple, I just don't know it (yet)
I'm using C# in Visual Studio
--[update]--
The whole url checking is in one single function Start()
try the background worker
If this is a desktop application that is performing these "web checks" then you can use a BackgroundWorkerThread to perform the processing, and get the results.
Or you could do something like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace ThreadWithDataReturnExample
{
public partial class Form1 : Form
{
private Thread thread1 = null;
public Form1()
{
InitializeComponent();
thread1 = new Thread(new ThreadStart(this.threadEntryPoint));
Thread1Completed += new AsyncCompletedEventHandler(thread1_Thread1Completed);
}
private void startButton_Click(object sender, EventArgs e)
{
thread1.Start();
//Alternatively, you could pass some object
//in such as Start(someObject);
//With apprioriate locking, or protocol where
//no other threads access the object until
//an event signals when the thread is complete,
//any other class with a reference to the object
//would be able to access that data.
//But instead, I'm going to use AsyncCompletedEventArgs
//in an event that signals completion
}
void thread1_Thread1Completed(object sender, AsyncCompletedEventArgs e)
{
if (this.InvokeRequired)
{//marshal the call if we are not on the GUI thread
BeginInvoke(new AsyncCompletedEventHandler(thread1_Thread1Completed),
new object[] { sender, e });
}
else
{
//display error if error occurred
//if no error occurred, process data
if (e.Error == null)
{//then success
MessageBox.Show("Worker thread completed successfully");
DataYouWantToReturn someData = e.UserState as DataYouWantToReturn;
MessageBox.Show("Your data my lord: " + someData.someProperty);
}
else//error
{
MessageBox.Show("The following error occurred:" + Environment.NewLine + e.Error.ToString());
}
}
}
#region I would actually move all of this into it's own class
private void threadEntryPoint()
{
//do a bunch of stuff
//when you are done:
//initialize object with data that you want to return
DataYouWantToReturn dataYouWantToReturn = new DataYouWantToReturn();
dataYouWantToReturn.someProperty = "more data";
//signal completion by firing an event
OnThread1Completed(new AsyncCompletedEventArgs(null, false, dataYouWantToReturn));
}
/// <summary>
/// Occurs when processing has finished or an error occurred.
/// </summary>
public event AsyncCompletedEventHandler Thread1Completed;
protected virtual void OnThread1Completed(AsyncCompletedEventArgs e)
{
//copy locally
AsyncCompletedEventHandler handler = Thread1Completed;
if (handler != null)
{
handler(this, e);
}
}
#endregion
}
}
If it's a web form look into AJAX.NET. There are several controls (UpdatePanel being one off the top of my head) that will help you do this.
Take a look at the toolkit.
EDIT: Only for web apps.
Application.DoEvents(); will do all the events that have happened up to that point.
so in your loop, after each website is checked, for example. do Application.DoEvents();
on the other hand if you just want to refresh your listboxes it'll be listboxname.Refresh();
both of these options, however will still have a time where it freezes while the website is pinged, unless you do many of them, which i dont suggets doing.
both methods also only use a single thread and is very linear.
The best option would be to create a new thread to do the tests on, or use a background worker that can do the tests on a seperate thread, so the events of the form can be handled instantly without a need to wait.
Manually controlling another thread shouldnt be too difficult.
here's an example.
using System.Threading;
public class MultiThreadingClass
{
private void FunctionForNewThread()
{
//do stuff
}
private void FunctionWithParameter(object param)
{
//Should do checks with typeof() on param before casting
int convertedparam = (int)param;
//do stuff
}
Thread t, t2;
static void Main()
{
ThreadStart ts = new ThreadStart(FunctionForNewThread);
t = new Thread(ts);
t.Start();
int x = 5;
ParameterizedThreadStart pts = new ParameterizedThreadStart(FunctionWithParameter);
t2 = new Thread(pts);
t2.Start(x);
}
}
it may be important to note here that you should never add a Thread as a local variable that will dissapear, as you can only really get the thread instance back by doing Thread.CurrentThread in the function which was called by the new thread, but if that thread has already locked up, you have a bit of a problem there :)
To easily handle Threads in a global variable either create an Array of threads and call Thread.Abort(); on each running thread when the program closes, or use the ThreadPool class in System.Threading.