How close window that opened in a thread in wpf? - c#

By specification i build an application that monitors certain resource. Now i have a main window that when an event occurs throws an alert that specification must be a big window with bright colors.
I gave solution to the problem as follows
private void StatusChanges(Alarm m, EventArgs e)
{
//Compares two 32-bit signed integers for equality and, if they are equal, replaces one of the values.
if (Interlocked.CompareExchange(ref running, 1, 0) == 0)
{
newWindowThread = new Thread(new ThreadStart(() =>
{
try
{
// Create and show the Window
Alert tempWindow = new Alert();
tempWindow.Show();
//if (cancelRun)
// tempWindow.Close();
// Start the Dispatcher Processing
System.Windows.Threading.Dispatcher.Run();
}
catch (Exception)
{
throw;
}
finally
{
running = 0;
}
}));
// Set the apartment state
newWindowThread.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
}
else
{
if (Interlocked.CompareExchange(ref running, 0, 1) == 1)
{
try
{
System.Windows.Threading.Dispatcher.FromThread(newWindowThread).InvokeShutdown();
Alert tempWindow = new Alert();
tempWindow.Close();
}
catch (Exception)
{
//throw;
MessageBox.Show("Todo esta ok");
}
}
//running = 0;
}
}
This method is executed from at interval by a Timer
my goal was when the alarm will end the process of removing the alert window, I used Thread.Abort (); did not work, and sometimes the program threw an exception and showed no visual studio in aplition happening outside, try using threa.Join (); but it didn't work, until
System.Windows.Threading.Dispatcher.FromThread use (newWindowThread) InvokeShutdown ().;
this at least stopped the showing up of the window, but when I create the
Alert tempWindow = new Alert();
in the initialize constructor
SoundPlayer player = new Sound ("../../Sounds/BOMB_SIREN-BOMB_SIREN-247265934.wav");
player.PlayLooping();
and when I close the window
player.Stop();
Now if not but I do the next
Alert tempWindow = new Alert();
tempWindow.Close();
the sound continues to play
I know that's not the best solution, but it works.
I would like to give me your opinion, and give me suggestions on how to improve the code.

Taking a quick look at your code, what you're doing is closing the temp. window incorrectly. You are creating a brand new Window when you want to close the existing one.
This should be updated so that the existing temp Window is saved so that that the same instance can be closed later.
I've also updated the Closing of the temp Window so that it's thread shutdown's after it has closed.
I updated the code as follows:
Alert _tempWindow;
private void StatusChanges(Alarm m, EventArgs e)
{
//Compares two 32-bit signed integers for equality and, if they are equal, replaces one of the values.
if (Interlocked.CompareExchange(ref running, 1, 0) == 0)
{
newWindowThread = new Thread(new ThreadStart(() =>
{
try
{
// Create and show the Window
_tempWindow = new Alert();
_tempWindow.Close += OnTempClosed;
_tempWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
catch (Exception)
{
throw;
}
finally
{
running = 0;
}
}));
// Set the apartment state
newWindowThread.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
}
else
{
if (Interlocked.CompareExchange(ref running, 0, 1) == 1)
{
try
{
_tempWindow.Dispatcher.BeginInvoke((Action)_tempWindow.Close);
}
catch (Exception)
{
//throw;
MessageBox.Show("Todo esta ok");
}
}
//running = 0;
}
}
private void OnTempClosed(object sender, EventArgs e)
{
System.Windows.Threading.Dispatcher.FromThread(newWindowThread).InvokeShutdown();
}

The idea is to work with the UI just from the one thread. So rather than creating a whole new dispatcher, why not simply dispatch the new window logic to the existing UI thread (after all, doesn't the timer already end-up on the UI thread all by itself?).
Why are you starting the new window in a new thread? What are you hoping to accomplish by that?
Your close code doesn't make sense:
Dispatcher.FromThread(newWindowThread).InvokeShutdown();
Alert tempWindow = new Alert();
tempWindow.Close();
Let's walk through this. First, you shutdown the dispatcher of the second UI thread. This means that all the messages that would flow to the window - don't.
Then, you create a new window, on the original UI thread, and you close that. What's that supposed to accomplish? You want to close the window that you've opened before, not a new window!
In practice, you need to keep a reference to the original window you've opened - the reference to it's thread isn't really important. Now, if you really want to keep using your two-UI-threads approach, you'd do this instead:
alertWindow.Dispatcher.Invoke(alertWindow.Close);
This will tell the message loop on the (already opened) alert window to close the window, as soon as it can.
With that out of the way, there's just too many things wrong with your code. For one, you're using way too low level operations (Interlocked.CompareExchange to maintain alert / no alert logic?). Two, your comments are completely useless - you're just saying what the next line does, literally - that's not helpful. Instead of
// Compares two 32-bit signed integers for equality and,
// if they are equal, replaces one of the values.
You want to use something descriptive of what you're trying to achieve, not how you're doing it:
// If we're the first ones to attempt to change the running flag,
// we'll create a new alert window.
Note how that comment also quite nicely shows that you're doing something very weird.
Three, you're needlessly creating threads (and even more so, UI threads - there should really only be one except for very specific circumstances). There's no reason whatsoever to create the new window in a new thread. Why are you doing that?
Four, your exception handling isn't very helpful either. For one, you can omit the catch clause if all you're doing is rethrowing the exception anyway - you can just use try ... finally alone. More importantly, the exception is going to be propagated to that new thread, not your old one - this will most likely cause your application to crash completely.

Related

How to shut down a second UI thread

I need to be able to start up a window on a second UI thread and shut it down again at will.
This is my current code:
/// <summary>Show or hide the simulation status window on its own thread.</summary>
private void toggleSimulationStatusWindow(bool show)
{
if (show)
{
if (statusMonitorThread != null) return;
statusMonitorThread = new System.Threading.Thread(delegate()
{
Application.Run(new AnalysisStatusWindow(ExcelApi.analyisStatusMonitor));
});
statusMonitorThread.Start();
}
else
{
if (statusMonitorThread != null)
statusMonitorThread.Abort();
statusMonitorThread = null;
}
}
AnalysisStatusWindow is a fairly basic System.Windows.Forms.Form
The above code is successfully creating the new UI thread, but my request to Abort the thread is ignored. The result is that toggling the above function multiple times simply results in new windows opening up - all of which are on their own thread and fully functional.
Is there any way I can pass a message to this thread to shut down nicely? Failing that, is there any way to make sure Abort() really kills my second UI thread?
I've tried using new Form().Show() and .ShowDialog() instead of Application.Run(new Form()), but they aren't any easier to shut down.
If anyone is questioning the need for a separate UI thread, this code exists in an Excel Add-in, and I cannot control the fact that the Excel UI blocks while calculations for a given cell are underway. For that reason, when a long running custom formula executes, I require this second UI thread to display progress updates.
Thanks to Hans for his comment. I solved my problem using the following code:
/// <summary>Show or hide the simulation status window on its own thread.</summary>
private void toggleSimulationStatusWindow(bool show)
{
if (show)
{
if (statusMonitorThread != null) return;
statusMonitorWindow = new AnalysisStatusWindow(ExcelApi.analyisStatusMonitor);
statusMonitorThread = new System.Threading.Thread(delegate()
{
Application.Run(statusMonitorWindow);
});
statusMonitorThread.Start();
}
else if (statusMonitorThread != null)
{
statusMonitorWindow.BeginInvoke((MethodInvoker)delegate { statusMonitorWindow.Close(); });
statusMonitorThread.Join();
statusMonitorThread = null;
statusMonitorWindow = null;
}
}

Progress window when doing computation in the UI thread

I would like to achieve this:
Create a new window, which is just a spinning loader and some static text, and display it.
Do some computation on the UI thread (it has to be done there)
Close the loader window
Currently, when I call .Show() on the loader window, it doesnt load at all, it just hangs out as a blank window (the UI thread is blocked, which I dont want)
What can I do to enable the loader window to display its contents at the same time my computation is done?
private void CopyToClipboard(object sender, RoutedEventArgs e)
{
var selected = mDataGrid.SelectedItem;
var selectedIndex = mDataGrid.SelectedIndex;
var progressWindow = mProgressDialog.Create(this,"Copying data to clipboard");
progressWindow.Show();
try
{
mDataGrid.ClipboardCopyMode = DataGridClipboardCopyMode.IncludeHeader;
mDataGrid.SelectionMode = DataGridSelectionMode.Extended;
mDataGrid.SelectAllCells(); // SLOW !!!!
ApplicationCommands.Copy.Execute(null, mDataGrid); //SLOW !!!!
}
catch (Exception ex)
{
mLog.Error("Copying to clipboard", ex);
MessageBox.Show("Error while copying to clipboard");
}
finally
{
mDataGrid.SelectionMode = DataGridSelectionMode.Single;
mDataGrid.UnselectAllCells();
this.Select(selectedIndex, selected);
progressWindow.Close();
}
}
As you said, your calculation is done on the UI thread, that's why your window is blank, it is not "repainted" due to the UI thread being busy.
So unless you do your long running calculation on another thread, you're quite stuck.
Only thing I was able to achieve is this:
progressWindow.Show();
this.DoEvents();
public static void DoEvents(this Window _)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
}
The "loader window" gets at least displayed, so the user knows something is happening. It usually lasts under a second, so this solution is acceptable to me.

what happens when we change the reference of a thread as null?

I am running a while loop which keeps track of some events forever, In case if i get any exception i am changing its reference to null hoping that the current thread will be aborted and the new reference of that thread will be created. Is it correct or any better way to abort the current thread and start a newer one.
I am trying to do this:
Thread th;
Main()
{
th = new thread(myfunction);
th.Start();
}
void myfunction()
{
while(true)
{
try
{
// something interesting here.
}
catch(exception)
{
th = null;
}
}
}
Only thing that will happen is that Thread will remain inaccessible from the Enclosing class.
If there are no further processing, doing so will make the thread out of reach from GC appllication roots. This makes object available for garbage collection in next GC trigger.
You need to do:
return;
instead of:
th = null;
Because the thread will keep on running. The thread object will not get collected, since it will stay referenced as long as the code is running.
Clean up anything you need to for that thread, then break out of the while loop like this:
void runningOnThread()
{
while (true)
{
try
{
//...
}
catch (Exception e)
{
break;
}
}
//thread cleanup code goes here, if you have any.
}
It would be a good idea to log the exception when you catch it. That way you know when you've hit an exception.
First, if you run into an exception, before worrying about starting a new thread, be sure that you actually handle the exception and ensure that the restarted thread will be able to run successfully. Otherwise, you're just going to get a constant stream of crashing threads, and a choppy program while it handles the exception parade. Just some food for thought.
Now, answering the question, best case nulling the reference to the thread will just leave you in an infinite loop, worst case you try to use 'th' later and you get an exception because it's null. Nulling the reference to the thread won't somehow make it aware that it needs to restart itself any more than nulling a reference to parameter you gave it as a function argument will. If you absolutely need some kind of ability to abort/restart the thread, look into doing one of:
raising an event when the thread crashes and break out of the while loop, or
setting a boolean/enum flag saying what the thread is doing, and have the main thread check on it every so often to make sure it hasn't been set to the error state.
This is code is completely off the top of my head, isn't that good, but will give you the general idea:
delegate void ThreadCrashedEvent();
Event ThreadCrashedEvent threadCrashed;
Thread th;
Main()
{
threadCrashed += OnThreadCrashed();
th = new thread(myfunction);
th.Start();
}
void OnThreadCrashed()
{
th = new thread(myfunction);
th.Start();
}
void myfunction()
{
while(true)
{
try
{
LetsGetDangerous();
}
catch(exception)
{
if(threadCrashed != null)
{
threadCrashed();
return;
}
}
}

NullReferenceException when closing a threaded form

Context :
I created an Windows Form application which runs a splash screen before starting. Quickly, here is what contains my Program.cs :
public static Thread splashScreenThread = null;
public static FormSplashScreen formSplashScreen;
[STAThread]
static void Main(string[] args) {
// Show splash screen
splashScreenThread = new Thread(new ThreadStart(ShowSplashScreen));
splashScreenThread.IsBackground = true;
splashScreenThread.Start();
// Load some components in background
LoadComponentsInBackground()
// Hide the splash screen
if (splashScreenThread != null) {
formSplashScreen.Invoke(new MethodInvoker(delegate {
formSplashScreen.Close();
formSplashScreen.Dispose();
}));
splashScreenThread = null;
}
// Start now the application
Application.Run();
}
private static void ShowSplashScreen() {
formSplashScreen = new FormSplashScreen();
formSplashScreen.ShowDialog();
}
Problem :
My problem does not happen everytime I start the application, it seems to be random and to occur more often on some PCs and less on others... So I'm a bit confused, besides I don't really understand where it comes from:
A NullReferenceException is raised on the formSplashScreen.Invoke(...Close...) line, but formSplashScreen is correctly initialized (I checked it while debugging).
I'm not sure if this comes from the thread or from another point...
Possible solution :
I could maybe surround the line causing problem with something like below, but it would only be getting round the problem and I'd rather like understanding it and properly solving it.
while (splashScreenThread != null) {
try {
formSplashScreen.Invoke(new MethodInvoker(delegate {
formSplashScreen.Close();
formSplashScreen.Dispose();
}));
splashScreenThread = null;
} catch (Exception e) {
}
}
This is a race condition that happens because the thread has been created and staterted but the SplashScreen has not been created yet.
In other words you're trying to close the splash screen before it was even created.
You can use EventWaitHandle to make sure that the SplashScreen has been created or at least wait until it's not null.
Another option is to signal the screen it should close and let him handle the closing logic.

Avoid calling Invoke when the control is disposed

I have the following code in my worker thread (ImageListView below is derived from Control):
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(
new RefreshDelegateInternal(mImageListView.RefreshInternal));
else
mImageListView.RefreshInternal();
}
However, I get an ObjectDisposedException sometimes with the Invoke method above. It appears that the control can be disposed between the time I check IsDisposed and I call Invoke. How can I avoid that?
What you have here is a race condition. You're better off just catching the ObjectDisposed exception and be done with it. In fact, I think in this case it is the only working solution.
try
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(new YourDelegate(thisMethod));
else
mImageListView.RefreshInternal();
}
catch (ObjectDisposedException ex)
{
// Do something clever
}
There are implicit race conditions in your code. The control can be disposed between your IsDisposed test and the InvokeRequired test. There's another one between InvokeRequired and Invoke(). You can't fix this without ensuring the control outlives the life of the thread. Given that your thread is generating data for a list view, it ought to stop running before the list view disappears.
Do so by setting e.Cancel in the FormClosing event and signaling the thread to stop with a ManualResetEvent. When the thread completes, call Form.Close() again. Using BackgroundWorker makes it easy to implement the thread completion logic, find sample code in this post.
The reality is that with Invoke and friends, you can't completely protect against invoke on a disposed component, or then getting InvalidOperationException because of the missing handle. I haven't really seen an answer yet, like the one farther below, in any of the threads that addresses the real fundamental problem, which cant be completely solved by preemptive testing or using lock semantics.
Here's the normal 'correct' idiom:
// the event handler. in this case preped for cross thread calls
void OnEventMyUpdate(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return; // ignore events if we arn't ready, and for
// invoke if cant listen to msg queue anyway
if (InvokeRequired)
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
else
this.MyUpdate(e.MyData);
}
// the update function
void MyUpdate(Object myData)
{
...
}
The fundemental problem:
In using the Invoke facility the windows message queue is used, which places a message in the queue to either wait or fire-and-forget the cross thread call exactly like Post or Send message. If there is a message ahead of the Invoke message that will invalidate the component and its window handle, or that got placed just after any checks you try to perform, then you are going to have a bad time.
x thread -> PostMessage(WM_CLOSE); // put 'WM_CLOSE' in queue
y thread -> this.IsHandleCreated // yes we have a valid handle
y thread -> this.Invoke(); // put 'Invoke' in queue
ui thread -> this.Destroy(); // Close processed, handle gone
y thread -> throw Invalid....() // 'Send' comes back, thrown on calling thread y
There is no real way to know that the control is about to remove itself fromthe queue, and nothing really reasonable you can do to "undo" the invoke. No matter how many checks you do or extra locks you make, you cant stop someone else form issuing something like a close, or deactivate. There are tons of senarios where this can happen.
A solution:
The first thing to realize is that the invoke is going to fail, no different than how a (IsHandleCreated) check would have ignored the event. If the goal is to protect the caller on the non-UI thread you will need to handle the exception, and treat it like any other call that didn't succeed (to keep app from crashing or do whatever. And unless going to rewrite/reroll Invoke facility, the catch is your only way to know.
// the event handler. in this case preped for cross thread calls
void OnEventMyWhatever(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return;
if (InvokeRequired)
{
try
{
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
}
catch (InvalidOperationException ex) // pump died before we were processed
{
if (this.IsHandleCreated) throw; // not the droids we are looking for
}
}
else
{
this.MyUpdate(e.MyData);
}
}
// the update function
void MyUpdate(Object myData)
{
...
}
The exception filtering can be tailored to suit whatever the needs are. Its good to be aware that worker threads often dont have all the cushy outer exception handling and logging the UI threads do, in most applicaitons, so you may wish to just gobble up any exception on the worker side. Or log and rethrow all of them. For many, uncaught exceptions on worker thread means the app is going to crash.
Try using
if(!myControl.Disposing)
; // invoke here
I had the exact same problem as you. Ever since I switched to checking .Disposing on the control, the ObjectDisposedException has gone away. Not saying this will fix it 100% of the time, just 99% ;) There is still a chance of a race condition between the check to Disposing and the call to invoke, but in the testing I've done I haven't ran into it (I use the ThreadPool and a worker thread).
Here's what I use before each call to invoke:
private bool IsControlValid(Control myControl)
{
if (myControl == null) return false;
if (myControl.IsDisposed) return false;
if (myControl.Disposing) return false;
if (!myControl.IsHandleCreated) return false;
if (AbortThread) return false; // the signal to the thread to stop processing
return true;
}
may be lock(mImageListView){...} ?
You could use mutexes.
Somewhere at the start of the thread :
Mutex m=new Mutex();
Then :
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
m.WaitOne();
if (mImageListView.InvokeRequired)
mImageListView.Invoke(
new RefreshDelegateInternal(mImageListView.RefreshInternal));
else
mImageListView.RefreshInternal();
m.ReleaseMutex();
}
And whereever it is you are disposing of mImageListView :
m.WaitOne();
mImageListView.Dispose();
m.ReleaseMutex();
This should ensure you cant dispose and invoke at the same time.
See also this question:
Avoiding the woes of Invoke/BeginInvoke in cross-thread WinForm event handling?
The utility class that resulted EventHandlerForControl can solve this problem for event method signatures. You could adapt this class or review the logic therein to solve the issue.
The real problem here is that nobugz is correct as he points out that the APIs given for cross-thread calls in winforms are inherently not thread safe. Even within the calls to InvokeRequired and Invoke/BeginInvoke themselves there are several race conditions that can cause unexpected behavior.
If a BackGroundWorker is a possibility, there's a very simple way to circumvent this:
public partial class MyForm : Form
{
private void InvokeViaBgw(Action action)
{
BGW.ReportProgress(0, action);
}
private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (this.IsDisposed) return; //You are on the UI thread now, so no race condition
var action = (Action)e.UserState;
action();
}
private private void BGW_DoWork(object sender, DoWorkEventArgs e)
{
//Sample usage:
this.InvokeViaBgw(() => MyTextBox.Text = "Foo");
}
}
Handle the Form closing event. Check to see if your off UI thread work is still happening, if so start to bring it down, cancel the closing event and then reschedule the close using BeginInvoke on the form control.
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
if (service.IsRunning)
{
service.Exit();
e.Cancel = true;
this.BeginInvoke(new Action(() => { this.Close(); }));
}
}
The solution proposed by Isak Savo
try
{
myForm.Invoke(myForm.myDelegate, new Object[] { message });
}
catch (ObjectDisposedException)
{ //catch exception if the owner window is already closed
}
works in C# 4.0 but for some reasons it fails in C#3.0 (the exception is raised anyway)
So I used another solution based on a flag indicating if the form is closing and consequently preventing the use of invoke if the flag is set
public partial class Form1 : Form
{
bool _closing;
public bool closing { get { return _closing; } }
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_closing = true;
}
...
// part executing in another thread:
if (_owner.closing == false)
{ // the invoke is skipped if the form is closing
myForm.Invoke(myForm.myDelegate, new Object[] { message });
}
This has the advantage of completely avoiding the use of try/catch.
One way might be to call the method itself ones more instead of invoking the ImageListView-Method:
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(new YourDelegate(thisMethod));
else
mImageListView.RefreshInternal();
}
That way it would check one more time before finally calling RefreshInternal().
The suggestion to stop the thread generating the messages is not acceptable. Delegates can be multicast. Because one listener does not want to listen to the band, you don't shoot the band members.
Since the framework doesn't provide any easy way I know of to clear the message pump of those event messages, and since the form does not expose its private property that lets us know the form is closing:
Set a flag on the IsClosing Event of the window after you unsubscribe or stop listening to the events, and always check this flag before you do a this.Invoke().
i have same error. my error occurred in thread. finally i write this method :
public bool IsDisposed(Control ctrl)
{
if (ctrl.IsDisposed)
return true;
try
{
ctrl.Invoke(new Action(() => { }));
return false;
}
catch (ObjectDisposedException)
{
return true;
}
}
This works for me
if (this.IsHandleCreated){
Task.Delay(500).ContinueWith(_ =>{
this.Invoke(fm2);
});
} else {
this.Refresh();
}

Categories

Resources