I'm new to development on Windows Phone 7 and Silverlight but I do have experience in win32 and threading in general.
Here's my question:
I am trying to "synchronize" the UI thread w/ another thread that seems to be used by the API's of the object that I am working with. In other words, I would like to make sure that before the user dismisses the current XAML page by pressing the back button, the object that I am working with, which is part of the C# class behind the XAML page is deallocated.
The reason for that is that if I have the deallocation code in the NavigatedFrom handler, the UI thread may attempt to release the object WHILE it is in fact used by the other thread. Therefore, I do have to somehow synchronize the deallocation of this object.
Ideally, when the user presses the back button on the phone, all I do is set a flag "quit" to true to indicate that the user intends to exit. The methods used by the object that are running on another thread, would "see" that this flag is set and then would BeginInvoke*emphasized text* the deallocation code of the object (only because the object has been allocated on the UI thread so I figured it makes sense to deallocate it on the same thread, not knowing its internal workings.) Finally, it would call NavigationService.GoBack() to ensure 'orderly' exit.
Unfortunately, I don't see a way of preventing the XAML page to be dismissed when the user presses the back button although I did override the NavigatedFrom and OnBackKeyPress methods. Even though they contain no code at all, the XAML page is dismissed anyway.
Another thing that is interesting and I would appreciate your comments on this, is that I have a timer (System.Windows.Threading.DispatchTimer). Would this timer be associated only with the C# class behind a XAML page that defines it? In other words, is there a concept of a "message pump" associated with each XAML pages or is there just one message pump for the UI thread that basically is used by ALL XAML pages ? I am asking this because although I dismiss the XAML page whose C# class defines the timer, it seems to still be running.
Thank you.
The reason for that is that if I have the deallocation code in the NavigatedFrom handler, the UI thread may attempt to release the object WHILE it is in fact used by the other thread. Therefore, I do have to somehow synchronize the deallocation of this object.
Not really a problem. If you queue the navigation on the Dispatcher as well, you don't get any NullReferenceExceptions.
Simply use Dispatcher.BeginInvoke(() => NavigationService.Navigate(...)) for safe navigation.
Would this timer be associated only with the C# class behind a XAML page that defines it?
If you by "class" means "ViewModel", then yes, it most definitively should be in the ViewModel.
Related
I found many answers say that accessing Qt Gui Widgets from another thread is not safe. I agree with this if we try to modify the widgets. But what if I only read the value of widget, without any modification?
I have designed a GUI tool using Qt, and my working thread reads Widgets directly, for example, get the text of QLineEdit, get the value of QComboBox,etc. And I haven't found any problem.
I pass the pointer of MainWindow to working thread.
Is this really not safe?
First: It is always a bad idea to read properties from another thread without protecting the memory (Mutex, Signal & Slot).
Your situation: If nothing wants to change the value, you should be fine. There are no problems if every thread only reads the value. But if you change the value of the QComboBox (for example) by clicking arrow up, arrow down or selecting a new item (what I expect you will do, because that's the reason of using a QComboBox) the value will be written and then the application can crash if your worker thread wants to read the value in the exact same moment (the possibility of a carsh depends on the frequenz your thread pulls the informations).
Your application never crashed because the condition never appeared.
But that does not mean that this can not happen.
At one point in my application, I have to save a JavaFX UI which takes a long time. During this save, I have to show a Progress Dialog telling the user what is happening at that time.
I have read that heavy tasks should be ran using a Task and not the JavaFX thread. However, this is not possible for me for the 2 following reasons:
1- The heavy tasks include JavaFX confirmation dialog popups which are sometimes buggy on MAC if not called by the JavaFX thread.
2- The save method must return a boolean to tell if the save went ok or not. And this save method is triggered by the JavaFX thread. Meaning the JavaFX thread must return the boolean variable and has to wait for the Task to finish before doing that.
And sadly the JavaFX UI is integrated in a Swing UI which makes it more difficult to work with.
Task is just one implementation of the javafx.concurrent.Worker interface, which provides facilities to communicate with the GUI respectively the JavaFX thread. Other implementations are Service and ScheduledService. Basically, the difference is that Service is designed for reuse, whereas Task isn't, and ScheduledService can restart itself after execution.
So, to address your two concerns:
The heavy tasks include JavaFX confirmation dialog popups which are sometimes buggy on MAC if not called by the JavaFX thread.
As mentioned before, the Worker interface provides a nice API to update the GUI from another thread. For instance, from the JavaFX thread, you can easily bind a Labeled to the message property which gets updated from the background thread.
The save method must return a boolean to tell if the save went ok or not. And this save method is triggered by the JavaFX thread. Meaning the JavaFX thread must return the boolean variable and has to wait for the Task to finish before doing that.
For example, Task's call() method can return arbitrary objects, which includes Boolean. Moreover, the Worker interface has a getValue() method to retrieve the result from the corresponding worker.
I recommend reading the "Concurrency in JavaFX" tutorial for further information. For ProgressBar itself, have a look at the "Progress Bar and Progress Indicator" tutorial.
Can someone explain to me what this error I'm seeing is?
Current thread must be set to single thread apartment (STA) mode before OLE calls can be made.
Specifically, I'm trying to open the SaveFileDialog/OpenFileDialog within C++/CLI on a form.
SaveFileDialog^ saveFileDialog1 = gcnew SaveFileDialog;
saveFileDialog1->ShowDialog();
if (saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK)
{
s = saveFileDialog1->OpenFile();
}
s->Close();
}
The error that is throwing is
An unhandled exception of type 'System.Threading.ThreadStateException' occurred in System.Windows.Forms.dll
Additional information: Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.
I'm not really familiar with what this error is saying. I know just a bit about threading, but I'm not sure how threading would be an issue here. I've seen some people reference things like STAThread without providing a clear explanation as to what it does, and Microsoft's documentation makes no mention of having this exception thrown when calling SaveFileDialog/OpenFileDialog, or how to handle it.
Thanks!
When you use OpenFileDialog then a lot of code gets loaded into your process. Not just the operating system component that implements the dialog but also shell extensions. Plugins that programmers write to add functionality to Windows Explorer. They work in that dialog as well. There are many, one you are surely familiar with is the extension that makes a .zip file look like a folder.
One thing Microsoft did when they designed the plug-in interface is to not force an extension to be thread-safe. Because that is very hard to do and often a major source of bugs. They made the promise that the thread that creates the plugin instance is also the thread on which any call to the plugin is made. Thus ensuring that the plugin is always used in a thread-safe manner.
That however requires a little help from you. You have to make a promise that your thread, the one that calls OpenFileDialog::Show(), observes the requirements of a single-threaded apartment. STA for short. You make the promise with the [STAThread] attribute on your program's Main() entrypoint. Or if it is a thread that you created yourself then by calling Thread::SetApartmentState() before you start it.
That's just a promise however, you also have to implement what you promised. Takes two things, you promise to never block the thread and you promise to pump a message loop. Application::Run() in a .NET program. The never-block promise ensures that you won't cause deadlock. And the message loop promise says that you implement a solution to the producer-consumer problem.
This should never be a problem, it is very unclear how this got fumbled in your project. Another implicit requirement for a dialog is that it must have an owner. Another window on which it can be on top of. If it doesn't have one then there are very high odds that the user never sees the dialog. Covered by another program's window, the user can only ever find it back by accident. When you create windows then you always also must call Application::Run() so the windows can respond to user input. Use the boilerplate code in a C++/CLI app so this is done correctly.
I routinely pass the main form handle to other threads so that they can post messages back to the main thread. I saw that on Sept 28, 2013, Remy Lebeau stated:
...the TWinControl.Handle property is not thread-safe, either. You
should use the TApplication.Handle property instead, or use
AllocateHWnd() to create your own window.
in this answer to a question about passing strings.
How is the handle property not safe? Does it change during the life of the program?
How is the Handle property not safe?
When you access the Handle property, if the window handle has not been created, then it is created on demand. If you access the Handle property from a thread other than the GUI thread, then this means you create the window on the wrong thread.
Does it change during the life of the program?
Yes, the window handle can change if the window is re-created.
I routinely pass the main form handle to other threads so that they can post messages back to the main thread.
In this case, you are probably not accessing the Handle property away from the main thread. It sounds like (although I cannot see your code) you are accessing Handle on the main thread and passing that value to the other thread.
However, window recreation is the problem for you. Since your window is subject to recreation, you simply cannot rely on that handle outliving your thread. Whether or not your window will ever be recreated is hard to predict. The VCL does not perform recreation lightly. However, in my view it is far better to be safe than sorry. So, use AllocateHWnd and take control of the lifetime of this window.
I have a multi-threaded Delphi 6 Pro application that I am currently working on heavily. If I set a breakpoint on any code that runs in the context of the Main thread (VCL thread) I don't have any problems. However, if a breakpoint is triggered on any code in one of my other threads, after I continue the application from the breakpoint, all repaints to the VCL components on the main thread (including the main form) don't happen anymore. The application isn't dead because other background code keeps running, just the main thread. It's as if the windows message dispatcher has been corrupted or rendered dormant.
Note, in this application I allocate my own WndProc() via allocateHwnd() on the main form because I need to catch certain registered messages. From that WndProc() I dispatch any custom messages I handle and if the current message is not handled by my code, I pass the message on by calling the main form's inherited WndProc(). If I do handle the current message I simply return from my WndProc() with Msg.Result set to 1 to tell the dispatcher that the message was handled. I can't simply override the TForm WndProc() instead of allocating my own WndProc() because for some reason the Delphi VCL does not pass through registered messages instantiated with the Windows API RegisterWindowMessage() call.
Has anybody experienced this in similar context and if so, what did you do to fix it?
-- roscherl
Since you call AllocateHWnd, that means you've created another window. You mustn't just take the messages that were addressed to that window and forward them to your form's window. Doing that, you're bound to screw things up in your program, although I'm not sure exactly how. Painting problems sound plausible. You should make sure it's really just painting problems and not that your main thread is still suspended. The debugger should be able to tell you that. (You should call DefWindowProc to make your allocated window handle messages you're not prepared to handle yourself. And returning 1 doesn't tell the dispatcher anything; the dispatcher doesn't care — whoever called SendMessage wants to know the result.)
I promise you that forms are completely capable of receiving registered window messages. Override WndProc or assign a new value to the WindowProc property (and remember to save the old value so you can call it after handling your own messages). The source of your problem lies elsewhere.
UPDATE: I'm not saying the way I got past the problem is a good solution. I need to take Rob Kennedy's notes and do some refactoring. However, to get past the problem for now I gave the thread it's own Window and WndProc() and at the top of the thread Execute loop I have a PeekMessage() while loop with calls to TranslateMessage() and DispatchMessage(). I no longer have a problem with setting breakpoints in the thread, but obviously this compounding of WndProc() methods indicates a structural problem in my code. I wanted to add this reply to fill out the discussion. I'm hoping that once I put Rob's suggestions to work when I clean up my WndProc() methods on the relevant forms, especially the main form, I can get rid of the this new WndProc() that I just added to the thread.
Robert.