views:

213

answers:

3

When running my program, sometimes I get these exceptions, other times I don't. This is the flow of execution:

User clicks a button in the Window1 class.
Class collects inputs, backgroundWorker.RunWorkerAsync() is started.
In the DoWork, App.doStuff() is called.
Inside App.doStuff(), I create another thread to show a progressbar in a different window like this:

Thread newWindowThread =  new Thread(new ThreadStart(showProgressMethod));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();

In the showProgressMethod(), I do:

progressWindow = new MyProgressWindow(this);
progressWindow.Show();
System.Windows.Threading.Dispatcher.Run();

Now, if I'm running it for the first time, I never have any problems. But when/if I redo the operation once it's completed, then I might get the exceptions.

Looking at the code, I get the feeling I'm doing something wrong by calling the dispatcher.Run() and the Thread.Start(), but I don't understand the threading model well, so I'm not sure.

Either way, on the first run, the program never does anything unusual. But sometimes when I click the button again to activate the process, then the exception occurs. I think it happens when the program hasn't had enough time to clean up? I'm not sure though.

EDIT

Seems that the problem is that the ProgressWindow thread isn't ending. Apparently adding a Thread.Abort() inside App.doStuff() or Dispatcher.Thread.Abort() inside the ProgressWindow fixes the problem. Apparently.

But, it's still raising exceptions because of the Abort() method, though it no longer crashes. I also don't understand why closing the ProgressWindow doesn't end the thread. And from what I've read using Thread.Abort() isn't good practice though again I don't understand why.

EDIT 2

Thread.Abort still had it crashing at times. So, what I've done is:
Replace the Show() call for ShowDialog(), as suggested and
Remove the call for the System.Windows.Threading.Dispatcher.Run();

A: 

What is inside MyProgressWindow? Maybe it accesses some ResourceDictionary twice, once from first newWindowThread, once from second? ResourceDictionarys cannot be shared between two threads.

Vlad
ProgressWindow only has a reference to mainWindow.backgroundWorker. The closing event in ProgressWindow calls backgroundWorker.CancelAsync() - if I close the ProgressWindow, I cancel the task. I commented those lines to stop it from doing anything and that didn't do it.
zxcvbnm
A: 

a) TargetInvocationException will the real exception in the InnerException property -- inspect that.

b) are you sure you aren't reusing/touching objects from two different threads? WPF explicitly throws if it detects a dependency property being accessed from a thread other than the one the dependencyproperty it was created on.

dhopton
well, when the progresswindow is closed i enable the main window, and when the backgroundworker thread ends i close the progress window. so i am accessing other threads resources, but i'm calling the dispatcher and using BeginInvoke, so i thought i was doing it the right way.
zxcvbnm
Yes, thats the right way. But make sure you've got the right dispatcher.
dhopton
A: 

User clicks a button in the Window1 class. Class collects inputs, backgroundWorker.RunWorkerAsync() is started. In the DoWork, App.doStuff() is called. Inside App.doStuff(), I create another thread to show a progressbar...

So now you have code running on the UI thread, code running on the BackgroundWorker thread, and code running on a new, third thread, one that's not the UI thread, and that's updating UI elements. Do I have that right? Because that sounds like it's almost certainly a problem.

Edit:

I'm surprised that what you're doing works at all, not that it works inconsistently.

The ProgressChanged event of the BackgroundWorker is what you want to use for interoperation between the method running in the background and the user interface. It runs on the UI thread.

Robert Rossney
yeah, that's what i'm doing. the code in the third thread controls whether the window is enabled or not - if the progressbar is active the window is disabled to prevent the user from starting the process more than once. how should i go about doing this then?
zxcvbnm
i'm already using progresschanged to update the progressbar in the progresswindow. but i still need to create the third thread to be able to show the progress window. i think. maybe there is a way to create the progress window in the same thread the ui uses, and then do the math with the backgroundworker, but i dunno.
zxcvbnm
You can just Window.Show() and it'll pump through the dispatcher. It's not a blocking call. ShowDialog(), however, is.
dhopton
Why can't you just make ProgressChanged show the window the first time it's called, and have the RunWorkerCompleted delegate take the window down?
Robert Rossney
@dhopton - I was running some more tests and noticed it still happened under some situations. With ShowDialog(), it seems to be no longer happening. Dunno why, but so far it's the best solution. @Robert Rossney - I've tried doing something along the lines you suggested, but it did not work. Perhaps I've done it wrong. If you post a more elaborate example, I'll try to convert it to my code and see if it works.
zxcvbnm