I have to use functionality that is in another application domain. The result should be displayed in user control.
I have something like that:
var instance = domain.CreateInstanceFromAndUnwrap(...);
instance.Foo(myWpfUserControl as ICallback);
Foo(ICallback itf) {
itf.SetData("...");
}
WpfUserControl.SetData(string data)
{
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(...)
...
}
I had to put [Serializable] attribute onto WpfUserControll class and implement serialization contructor as well as ISerializable interface but now i receive exception:
The calling thread must be STA because many UI components require this
that is raised from UserControl() constructor
What shall I do to avoid this ?
Thank you in advance !
==============================
Solution
as #Al noticed, my user control have to be serialized when it comes to cross-application-domain calls. Now i pass proxy, that implements ICallback interface. Proxy was marked with Serializable attribute.
Proxy implementation should have absolutely no knowledge about user control as there should be an attempt to deserialize user control instance once again. When I tried to abstract proxy from user control via interface it didn't help. When i tried to pass interface to proxy (that was implemented by user control) - same exception occured.
Finally I decoupled proxy and user control with queue/semaphor. Queue was monitored by a worker thread that deligated calls to user control
p.s. this queue should be inherited from "MarshalByObjectRef".
If the exception is coming from the constructor, it means that you're not creating this control instance from the UI thread. This can be fine but you have to make sure the Thread is an STA thread by calling .SetApartmentState(ApartmentState.STA) on the thread object before the thread is started.
This also means you have to have access to the thread object before its started so you cant do this on a threadpool thread.
The best way to avoid the problem though is probably to create the control on the main UI thread and then assign the Text value using the Dispatcher (or a Task on the UiScheduler). That way you'll also avoid problems if the main thread needs to set, get or bind to the control, as that would cause a cross thread exception if the control was created on another thread
i'd advice against seriealizing the control this way if possible. doing that will generate a new object that is not attatched to any panels or some such, and the original control would not be updated. sadly you cant inheirit from MarshalByRefObject that would eliminate serialization since it would only pass a reference to the other domain.
If you can, call Foo separately and then pass the result to SetData in the original Appdomain
Related
I'm converting a UI from windows forms to WPF. I'm getting the following exception "The Calling thread cannot access this object because a different thread owns it" whenever I try to call anything on this new WPF window I created.
I referred stack overflow and many websites to find out that I should use Dispatcher.CheckAccess() or somethings similar to dispatcher and check access. I tried many such things
This is one of the things that I have used
Private Delegate Sub ShowSkinInvoked()
If (Dispatcher.CheckAccess()) Then
Me.Show()
Else
Dim d As ShowSkinInvoked = New ShowSkinInvoked(AddressOf ShowSkin)
Dispatcher.Invoke(d)
End If
This has removed the exception and while debugging the error is gone but it freezes the application and I cannot do anything other than terminate it. It doesn't even show the window after "Me.Show".
Also, if I compile the program and then make the calling module use this compiled exe by specifying path to exe then for some reason it works perfect.
If this sounds confusing then what I mean is, I have multiple forms. If I call the code in module A to load and display module B then it gives me the exception but if I call the code in module A to run the compiled exe of module B then it runs perfectly.
Any suggestions?
When WPF creates a user interface it created a thread that is responsible for handling all the user interaction events and scheduling the rendering. This is called the dispatcher thread. Many of the objects that it creates are sub classes of DispatcherObject.
You can't call methods on a DispatcherObject from threads other then the Dispatcher thread that created them. The reasons why are complicated but relate to COM interop.
When you are in a Buttons click event you are running on dispatcher thread.
If you are coming from another thread you must get your work to be performed on the dispatcher thread. It can typically be found by accessing the static current dispatcher Dispatcher.CurrentDispatcher, unless your creating multiple dispatcher threads.
However I would suggest explaining your problem in terms of what work your trying to do with regards to having one form show ui on another. There are multiple ways like an EventAggregator to communicate between ui that might be more appropriate.
This method call sits inside a class derived from DispatcherObject:
Dispatcher.Invoke(DispatcherPriority.Input, new ThreadStart(() =>
{
var exported = formatProvider.Export(original.Workbook);
Workbook = formatProvider.Import(exported);
}));
The method on the class is called by a backgroundworker in its DoWork delegate.
Workbook is Telerik's Workbook, as used by the RadSpreadsheetControl. Obviously, workbooks can only be accessed by the UI thread.
The above code throws an InvalidOperationException, saying
The calling thread must be STA, because many UI components require
this.
I don't really understand, as I thought that when invoking the actions with a Dispatcher, I would be calling it from the UI Thread, which is STA?
What am I missing here and how can this be fixed? Or should this work in general and the bug is somewhere else? What could be a reason then?
TL;DR: You must create this DispatcherObject inside your UI thread, not in a worker.
DispatcherObject.Dispatcher, which you are marshalling the operation to, is set to Dispatcher.CurrentDispatcher at the time of the object's construction. If the object is not created inside your existing UI thread then the documented behavior of CurrentDispatcher is to create a new dispatcher object associated with the thread. Later on, Invoke tries to marshal the call to that thread (which is not STA) resulting in the error.
It is not sufficient to use a class derived from DispatcherObject. You must use the Dispatcher from an existing UIElement created from XAML (or at least make sure, you create your class from inside the GUI thread where it picks the right Dispatcher).
I have a form and several external classes (serial port, file access) that are instantiated by the form.
1) What's the simplest way to run an instance of an external class in its own thread?
2) Is the instance's thread automatically terminated when the form closes?
1) What's the simplest way to run an instance of an external class in its own thread?
Instances of classes do not "run". Methods do.
As such, you may want to look into the APM pattern and the BackgroundWorker class.
2) Is the instance's thread automatically terminated when the form closes?
It depends on how the threads were started. A thread can be a background thread or a foreground thread - the latter prevents the application from terminating.
If it's just a couple of lines of code you want to call asynchronously, probably the best way is ThreadPool.QueueUserWorkItem. See: What's the difference between QueueUserWorkItem() and BeginInvoke(), for performing an asynchronous activity with no return types needed
See if you are working with managed Environment, when an object is instantiated it will automatically dispose off if it is out of scope. The Disposal is actually taken care of by Garbage collection.
If you are using UnManaged objects, its your responsibility to close resources before making the object out of scope.
Garbage collection periodically turns on and start collecting all the objects that are out of scope. If you need to work on large objects, you can try using WeakReference class which will hold the object but also expose it for Garbage collection.
Read about WeakReference and garbage collection from here:
http://www.abhisheksur.com/2010/07/garbage-collection-algorithm-with-use.html
I hope this would help you.
I am working through the requirement to make a WPF Application single instance only.
However - I have to pass the command line to the first instance and then perform some UI action.
I am using a Mutext to check for already running instances, I do use NamedPipes to transfer the command line to the already running instance.
But of course I am not in the correct Thread to access "Window1".
I tried to store a reference to "Window1" in a static class and then use the Dispatcher to call a Method in "Window1", however, as soon as I try to access a variable (class wide scope in "Window1") I receive a "Object reference not set to an instance of an object."
The UI Action is to add a new Tab to a TabControl - during initialization of the new Tab some work is done - and the variables are initialized and even the method I want to call works during the init - but when called from the Dispatcher it fails.
Any hints, how to do this? Am I on the wrong track here?
Thanks!
This is easy:
void ProcessCommandLine(string commandLine)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
... code to process the command line here ...
});
}
You can call this from your App.Startup and also from your thread that receives messages from the named pipe.
The key considerations here are:
Use of BeginInvoke instead of Invoke to prevent the calling thread from waiting
Use of DispatcherPriority.ApplicationIdle to guarantee the application has finished initializing before the command line is processed
Use of Application.Current.Dispatcher instead of Window1.Dispatcher in case Window1 has not yet been initialzed
That's not right, are you certain that the mutex is passing control correctly to your currently running instance of the application?
If it was a thread UI access issue, you should have received this error: The calling thread cannot access this object because a different thread owns it.
The fact that you're getting an "Object reference not set to an instance of an object." error message means that you've not yet instantiated the object as new.
In a WPF application I had a BackgroundWorker thread creating an object. Let's call the object foo.
Background worker code:
SomeClass foo = new SomeClass();
// Do some operation on foo
// Set some dependency property on the main class to foo
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(SendOrPostCallback)delegate { SetValue(FooProperty, foo); },
foo);
Now, when the main class tries to access FooProperty using a getter, I get an InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
If the thread the create the object is finished, why does it still own the object? Is there someway around this.?
An object you create in a given thread is owned by that thread. That's why you're getting an InvalidOperationException. If you would like to set a property in a background thread, I recommend having the delegate return the desired value and calling SetValue from your main thread. (thread that created the object).
The vast majority of objects in WPF have thread affinity. That is once created on a particular thread, a WPF object will then only operate when used from that thread. This restriction is enforced (unlike WinForms) on practically every operation you do on that object.
You will need to change your code to have Foo created on the actual UI thread for this scenario.
Assuming SomeClass is derived from DispatcherObject, the thread that created it runs the message pump that is responsible for processing messages for the foo object. Therefore if that thread ends then the object can no longer process messages, which isn't a good idea.
In most situations you should use the same UI thread to create all of your UI objects (or anything else that is derived from DispatcherObject and make sure it runs for the duration of your application. Then use the Dispatcher to send messages to it from your worker threads.