I am converting a chat parser for a game i play that i wrote in c# winforms over to wpf, mainly just to get a better handle on MVVM and wpf. Here is a run down of how i have my project set up
View:
For now its just a simple ListBox with ItemSource bound to my viewmodels observable chat collection
Model:
I have multiple characters that can be logged in at one time and each character has a chat class. The chat class starts a background worker that grabs and next line of chat from the game and fires off an event called IncomingChat with this line.
public event Action<Game.ChatLine> IncomingChat;
I'm using a background worker to fire an event in my backgroundworkers progresschaged event because when i was using a timer i kept getting a threading issue. At first i corrected this by changing my Timer to a DispatchTimer, but this didn't seem right to me to have a DispatchTimer in my model.
ViewModel:
Since i have multiple characters i am creating multiple ChatViewModels. I pass a character into the ChatViewModels constructor and subscribe to the Chat event. I create a ObservableColleciton to hold my chat lines when this event is received. Now I'm receiving a threading issue on my viewModel when trying to add the line i receive from my chat event to my observablecollection.
I got around this by making my viewmodels incoming chat event handler look like so
public ObservableCollection<Game.ChatLine) Chat {get; private set;}
void Chat_Incoming(Game.ChatLine line)
{
App.Current.Dispatcher.Invoke(new Action(delegate
{
Chat.Add(line)
}), null);
}
This doesn't feel right to me though. Although it works, using Dispatcher in my viewmodel like this seems out of place to me.
Although it works, using Dispatcher in my viewmodel like this seems out of place to me.
This isn't a completely unreasonable approach, and is the approach that many people take. Personally, if you're using WPF (or Silverlight 5), and have access to the TPL, I prefer to use the TPL to handle this.
Assuming your ViewModel is constructed on the UI thread (ie: by the View, or in response to a View related event), which is the case nearly always IMO, you can add this to your constructor:
// Add to class:
TaskFactory uiFactory;
public MyViewModel()
{
// Construct a TaskFactory that uses the UI thread's context
uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
}
Then, when you get your event, you can use this to marshal it:
void Chat_Incoming(Game.ChatLine line)
{
uiFactory.StartNew( () => Chat.Add(line) );
}
Note that this is slightly different than your original, since it's no longer blocking (this is more like using BeginInvoke instead of Invoke). If you need this to block until the UI finishes processing the message, you can use:
void Chat_Incoming(Game.ChatLine line)
{
uiFactory.StartNew( () => Chat.Add(line) ).Wait();
}
A View Model is a good place to do a Thread Synchronization. Remove the DispatcherTimer from your model and let the VM handle it.
I love Reed's answer, and agree with your concerns that something isn't right with your use of the Dispatcher. Your VM references App, which is, in my mind, a reference to a UI artifact (or control). Use Application instead or, better yet, inject the correct Dispatcher instance into your VM, which avoids the need to instantiate your VM in the UI thread.
Related
In a WPF application I'm working on I came across following code snippet;
public int SomeInteger
{
get { return _someInteger; }
set
{
_someInteger= value;
OnPropertyChanged("SomeInteger");
SomeIntegerChanged();
}
}
As you can see, inside the property setter a method is called. Is this approach correct or is there any better way to do in MVVM pattern WPF?
As above code snippet is a sample, actually the property setter might load a DataTable when the property changes which might be time consuming. I tried to make the application asynchronous using async and await but above scenarios causes problems since async and await doesn't have asynchronous property concept.
I also got across a way to bind a event to INotifyPropertyChanged and by checking which property triggers the event and call the method. But looking for any other alternatives.
Is this approach correct or is there any better way to do in MVVM pattern WPF?
This is, unfortunately, the common and accepted MVVM pattern across all of C#. I say "unfortunately" because this pattern does run into problems. It is based on events - which are problematic just on their own - and it also causes an immediate update, which in many cases is not desired.
For one example, sometimes it's desirable to have two properties that are mutually dependent - when the user changes one, the other one changes in relation to it - and this can cause problems since there's no way to distinguish "user changes" from "code changes".
For another example, sometimes you can end up with a whole tree of dependent properties, and it can take some time for all the changes to propagate through and stop interfering with each other.
More modern MVVM approaches like the atomically-updated single source of truth with single-directional data flow (as popularized by Redux) avoid the above problems. There is a C# Redux implementation; I don't know how easy it is to use. In my own CalculatedProperties library, I built my own "invalidation queue" to work around this, which defers all PropertyChanged updates until after the entire system's new steady-state is calculated.
I tried to make the application asynchronous using async and await but above scenarios causes problems since async and await doesn't have asynchronous property concept.
True. In particular, you can't "asynchronously get" a property. And this makes sense in the MVVM world; when WPF is updating the screen, and it asks your VM for a data value, then your VM can't say "hold on, I'll get it in a minute". WPF needs to know that value now so it can update the screen now.
Hooking up an asynchronous event handler is one way of kicking off asynchronous work when the value changes; that approach is fine. Usually, the presence of SomeIntegerChanged implies that there is a "changed" event specifically for that property, so hooking into PropertyChanged with string comparisons is probably the harder way to do it.
For more on asynchronous properties with MVVM, see my article on the subject.
In this case, calling the methods in the Setter is okay. Using INotifyPropertyChanged for property change notifications in WPF is the correct way, and adding the call to the Setter is the default way to implement it.
The call to SomeIntegerChanged(); depends a bit on what the method does (and if it's even required given you already implemented INotifyPropertyChanged).
Using properties as a consumer should have as little side-effects as possible, and generally should be simple operations. There's a nice guide on MSDN about that topic.
You should also avoid using strings of property names as parameters, in your case the call to OnPropertyChanged should look like this: OnPropertyChanged(nameof(SomeInteger));
A property setter shouldn't kick off a long-running operation. It should simply set the value of a backing field and perhaps call a fast synchronous method that returns just almost immediately. Please refer to #Stephen Cleary's blog post for more information.
If you need to call an asynchronous whenever SomeInteger gets set, you could for example hook up an async event handler for the PropertyChanged event, e.g.:
this.PropertyChanged += async (s, e) =>
{
if (e.PropertyName == nameof(SomeInteger))
{
await SomeAsyncMethod();
}
};
I would caution against premature optimisation.
Don't complicate your solution any more than is necessary.
If the change is immediate then there's no problem putting what you like in a setter.
Dependent property changes are an occasional requirement but also not usually a big deal.
If you have a long process needs to start then that sounds like it should be something discrete. You should initiate that processing on a separate thread and or decouple the processing. You could just start up a fire and forget thread ( not terribly elegant ).
You can decouple processing using service calls, message queues like msmq or RabbitMQ or pub sub. There are many pub sub approaches - a simple implementation being mvvmlight's messenger or prism's eventaggregator. More sophisticated would be NServiceBus or even Windows Workflow. If events are involved then weak events are preferable.
A common exception one can get when working with multiple threads in WPF is:
The calling thread cannot access this object because a different thread owns it
What are the options to deal with this properly?
Depending on the situation there are various options:
Accessing a control from another thread
e.g. updating a TextBlock with progress information.
Data Binding:
In this case the easiest thing you can do is avoiding the direct interaction with the control. You can just bind the property you want to access or modify to an object whose class implements INotifyPropertyChanged and then set the property on that object instead. The framework will handle the rest for you. (In general you rarely should need to interact with UI-elements directly, you can almost always bind the respective properties and work with the binding source instead; one case where direct control access may be necessary is control authoring.)
There are some cases where data binding alone is not enough, for example when trying to modify a bound ObservableCollection<T>, for this you need...
Dispatching:
You can dispatch your accessing code to the thread owning the object, this can be done by calling Invoke or BeginInvoke on the Dispatcher owning the object being accessed (getting this Dispatcher is possible on another thread).
e.g.
new Thread(ThisThreadStart).Start();
void ThisThreadStart()
{
textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test"));
}
If it is not clear on which thread a method is executed you can use Dispatcher.CheckAccess to either dispatch or execute an action directly.
e.g.
void Update()
{
Action action = () => myTextBlock.Text = "Test";
var dispatcher = myTextBlock.Dispatcher;
if (dispatcher.CheckAccess())
action();
else
dispatcher.Invoke(action);
}
If an object is not a DispatcherObject and you still need the associated Dispatcher you can use Dispatcher.CurrentDispatcher in the thread creating the object (so doing this in the method being executed by a thread will not do you any good). For convenience as you usually create objects on the application's main UI thread; you can get that thread's Dispatcher from anywhere using Application.Current.Dispatcher.
Special cases:
BackgroundWorker
Move any control access to ProgressChanged as it occurs on the thread that created the instance (which should of course be the UI-thread)
Timers
In WPF you can use the DispatcherTimer for convenience, it does the dispatching for you so any code in Tick is invoked on the associated dispatcher. If you can delegate the dispatching to the data binding system you of course can use a normal timer as well.
You can read more about how the Dispatcher queue works and WPF threading in general on MSDN.
Accessing an object created on another thread
e.g. loading an image in the background.
If the object in question is not Freezable you should in general simply avoid creating it on another thread or restricting access to the creating thread. If it is Freezable you just need to call Freeze to make it accessible to other threads.
Accessing a data object from another thread
That is, the type whose instance is being updated is user-code. If an exception is thrown this situation probably came about by someone using DependencyObject as base type for a data class.
This situation is the same as accessing a control and the same approaches can be applied but usually it should be avoided in the first place. Granted, this allows for simple property change notifications via dependency properties and those properties can also be bound but often enough this is just not worth giving up thread-independency. You can get change notifications from INotifyPropertyChanged and the binding system in WPF is inherently asymmetrical, there always is a property that is bound (target) and something that is the source for this binding. Usually the UI is the target and the data is the source, meaning that only UI components should need dependency properties.
That would be several hundred lines of code, for something I "figured out".
But the summary is:
App_OnStartup
generate a background thread
in the callback,
Call
Application.Current.MainWindow.Dispatcher.CheckAccess() - gets the exception
Application.Current.Dispatcher.CheckAccess() does not
I have a udp listener object that communicates through events where the method/callbacks are +='ed in my mainWindow wpf .cs file.
The event handler functions are called with parameters, one being the message I want displayed in a listbox in the mainWindow.cs
Using the information in this thread by H.B. above;
I have added, tested and handled the crossthread in wpf in my eventhandler callback using the following code, but I use a real message not a hard coded one:
listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere")));
UPDATE:
This is better because you can put more things in the anonymous function.
listBox1.Dispatcher.Invoke((Action)delegate
{
listBox1.Items.Add(e.ReaderMessage);
});
In my WPF try to separate my classes logic from any interface related data and only supply ObservableCollection properties for binding.
Problem is, when I access those binded OCs from other threads, I am required to do so through the dispatcher. The result is that I am required to add many Dispatcher.Invoke() calls, hidden inside my classes, whenever one of the methods attempts to update the OCs.
How can I do that in a more clean and separated way, so the dispatcher calls be abstracted away from my methods?
I don't have a silver bullet. But if you are certain and ready to take the responsibility of implicit UI delegation, you can always inherit from ObservableCollection, override methods and dispatch all requests to UI.
But the following code makes me scary:
// somewhere in thread pool:
for(int i = 0; i < 1000; i++)
{
_dispatcherAwareCollection.Add(i);
}
It seems innocent, but under the hood it blocks calling thread 1000 times. Alternatives might be your specific BulkXXX() methods, that will delay notification until all elements are processed. This solution is not perfect either, since you wanted an abstraction that could let you seamlessly swap collections, but BulkXXX() methods are very specific to new collection.
Option 1
I think you should look into a better separation of your code using the MVVM pattern, if you aren't familiar with it, I highly suggest to see the following video as it explains exactly what you're looking for.
Specifically, however, in your case you should have the model class with regular collection (e.g List) on which you do all the work in the threads. Your ViewModel should contain the ObservableCollections and connect loosely with the collections that exist in the model, e.g, you can choose to subscribe via an event from your ViewModel to a certain update logic in your model. You will STILL need to use Dispatcher to update the OC, but you will only need to do it once.
Option 2
You can instead just use the solution described here. Basically, he created a new derived class from OC that allows you to dispatch changes from the code automatically without you ever needing to update the dispatcher yourself.
The common approach is to have a Dispatcher property on your view model (probably in a base class for all view models) that can be injected outside. It is OK to have it in a view model because view model SHOULD be aware of UI concepts, but it should not be aware of particular view (layout, controls, etc.) and certainly it should not have a reference to the view.
What you can do is you can make it easier to dispatch your code to the Dispatcher thread by creating a helper or a service that will abstract the dispatcher away. For example, you can create a helper like this:
public class AsyncHelper
{
public static void EnsureUIThread(Action action)
{
if (Application.Current != null && !Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.BeginInvoke(action, DispatcherPriority.Background);
}
else
{
action();
}
}
}
And whenever you need to update you observable collection, you wrap you code in that helper method:
AsyncHelper.EnsureUIThread(() =>
{
// Update you observable collections here
});
OR, you can go further and use AOP (e.g. PostSharp) to specify declaratively (using attributes) that a method should be executed in the UI thread.
And finally, please note that you have to dispatch only collection updates to the UI thread. Usual properties can be safely updated from a background thread. The updates will be dispatched to the UI thread automatically by the binding mechanism. Probably in future versions of WPF updates to a collection from a background thread also will be supported.
Well you could write yourself an AsyncObservableCollection, if you know how to write it threadsafe. Then you can encapsulate the Dispatcher calls in it.
The problem is you would not use the standard ObservableCollection delivered within the .Net - Framework. It would increase the risk of errors in your application.
Another option would be to implement a WrapperClass, which contains and exposes an ObservableCollection for binding and has methods to modify the collection.
public class WrapperClass<T>
{
public ObservableCollection<T> Collection {get; set;}
public void Add(T item)
{
//do your dispatcher magic here
}
...
}
To modify the collection you implement the methods in it. The problem here is, that there is no guarantee, that others will use these methods, too.
I am afraid that you will have to wait for the next version of wpf
From this post:
A few nuggets we can expect to see in the next version of WPF include:
Hosting of Silverlight content with the new SilverlightHost element, without airspace issues (the inability to overlap WPF content over native Windows hWnd content)
Overall better management of airspace with hosted native hWnd-based content like the WebBrowser, HwndHost and WindowsFormsHost
Enabling binding and change notification for collections that are created on a background thread
Better integration with UI virtualization
Integration of the Ribbon control
And more
Use SynchronizationContext instead of Dispatcher. SynchronizationContext is common feature for threads synchronization in .NET, meanwhile Dispatcher is intentionally developed for WPF.
You probably want to use something like MTObservableCollection. I've used this in a project and it worked fantastically. Basically, it does all the dispatching work for you when the collection changed event is raised, by analysing the thread that the handler was assigned from, and dispatching accordingly.
The article is well worth a read, even if you don't plan to take this option.
I have an extension for this:
public static class DispatcherInvoker
{
public static void AddOnUI<T>(this ICollection<T> collection, T item)
{
Action<T> addMethod = collection.Add;
Application.Current.Dispatcher.BeginInvoke(addMethod, item);
}
}
EDIT:
I stole it from an stackoverflow post but forgot from which one
I think you have to much coupling if you need to think about threading in your model layer.
What you should do is to not connect your model directly to the GUI. As others have said, use a layer in between (MVVM).
This means that you let your MVVM layer respond to the change notifications from your observable collection. It is the MVVM layer that decides if and how these notifications should be passed on to the GUI. Look here for a way to lower the update frequency of the GUI to keep it usable.
In short:
Keep using an ObeservableCollection in your model layer if you like but don't use it directly in the GUI binding. Let another layer receive the notifications and control the GUI update.
I have an application wherein I would like a function to be executed in the same thread when an event is fired. For example:
SomeCode()
{
// Do something...
// Fire event to run SomeOtherCode().
}
SomeOtherCode()
{
// Do something else...
}
I do not want to simply call the function because it will hold things up. SomeOtherFuction() needs to be executed in the same thread because it needs to access the form controls, and I need it to begin execution from an event trigger firing. I am using Microsoft Visual C# 2008 Express Edition. Thanks.
::: EDIT:::
Additional Details: The bottom line is that the contrustor of my form application is taking far too long to complete, and it is causing a significant delay, from when the user launches the application to when the application window appears on the display. This is not a problem on faster computers, but on slower computers it is a big problem. I need to exit the contrustor as soon as possible, thus allowing the framework to draw the application window, and continue initialization outside the constructor. (All essential items would still be initialized inside the constructor.)
An event-triggered function call would be ideal. I would prefer not to use a timer. Interlacing the affected code with Invokes is impractical in my situation and would require much more time to implement than I have to work on this. A simple example of an event-driven function call is all I'm really looking for. Thanks.
From your posts it's seems like you're confusing a few issues. The standard pattern in .Net is for events to run synchronously. The following lines are essentially identical in terms of when they execute.
Option #1
SomeCode();
SomeOtherCode();
Option #2
SomeEvent += delegate { SomeOtherCode(); }
...
SomeCode();
SomeEvent(this,EventArgs.Empty);
If you want to unblock the UI thread and run the code later you'll need to use some mechanism to delay the running of the SomeOtherCode function. The easiest way to do this in a WinForms application is to use a WinForms Timer instance. This will raise an event on the UI thread at a later point in time that you can respond to. It also won't block the UI thread during this time allowing your form to continue processing.
You seem to be asking to run SomeOtherCode() later.
You can call BeginInvoke (either from the UI thread or from any other thread) to queue a function to run during the next message loop:
BeginInvoke(new Action(SomeOtherCode));
It seems that you would want to add an event to the class that exposes the SomeCode method. Then, the class that implements the SomeOtherCode method would attach an event handler that calls the SomeOtherCode method.
It's completely viable to have this done in one class, in case you have some sort of state model where you want to add/remove the call depending on some other logic.
I think you want to put SomeOtherCode into a Task or BackgroundWorker, which would then synchronize with the UI thread to send it updates.
I recently posted on my blog a class that makes updating the UI from a Task as easy as from a BGW. I do recommend using Task rather than BackgroundWorker.
Simialr to what Stephen said, I would recommend that you move as much of that initialization code to a background thread or task. Let the background thread do as much work as possible, then send the necessary window updates to your UI thread via Action<>'s. Here's some quick psuedo-sample code:
protected void LoadMyListInBackground(object state)
{
List<string> myList = Databse.FetchMyList(myParameters); // This take a while, so the UI thread isn't waiting
ShowMyList(myList);
}
protected void ShowMyList(List<string> theList)
{
if(InvokeRequired)
Invoke(new Action<List<string>>(ShowMyList, theList);
else
{
foreach(string item in theList)
myListBox.Items.Add(item);
}
}
In this example the UI thread is free to keep drawing your window while the background thread does the lengthy database work. The problem is, even if you fire an event outside of your constructor, and that event occurs on the UI thread and takes a long time, the user might see the window but that window is going to 'freeze' and possibly appear to be 'crashed' to the user. This technique prevents that and provides a better user experience.
I have a class that handles events created by multiple member objects. Events from these objects spawn worker threads for the event, so that the various event handlers in my class are running on different threads (one is a serial handler, one is a timer event, etc.) I'm looking for a simple way to make my code thread-safe, preferably by forcing the event handlers to run on my object's thread.
If this were a Forms UI object, I could take advantage of its implementation of the ISynchronizeInvoke interface, and make calls to InvokeRequired, Invoke, etc. In WPF I could use a Dispatcher object. But my class needs to run *independently of any UI code.
Here's a simplified example of what I have:
public class MyClass
{
private SomeObject object1;
private AnotherObject object2;
public MyClass()
{
object1 = new SomeObject();
object2 = new AnotherObject();
object1.AThreadedEvent += ThreadedEventHandler1;
object2.AnotherThreadedEvent += ThreadedEventHandler2;
}
// This runs in its own thread!
private void ThreadedEventHandler1()
{
// DO STUFF HERE
}
// This runs in its own thread!
private void ThreadedEventHandler2()
{
// DO STUFF HERE
}
}
Because both event handlers access the same objects in the parent class (including each-other!), it would be awesome if there were a simple way to force the event handlers to run in the creating object's thread.
I've toyed with the idea of having my class implement the ISynchronizeInvoke interface, but it appears that doing so can get pretty complicated. Before I jump down that rabbit hole, I thought I'd ping the experts to see if there is a more simple solution.
Thoughts?
EDIT:
Part of the reason I want to run the event handlers in the parent object's thread is because the parent object has it's *own events that are triggered based on the events sent by its member objects. I'd like any threading functionality to be hidden by this class, so that code that uses the class doesn't have to worry about thread-related issues (ie. locks and so on). Simply locking shared data won't do the job, because I *still need to trigger events from within the threaded event handlers.
The ideea of invoking on another thread is hand to hand with having a while loop that from time to time it checks whether there is an "outside" message to be processed. For UI, there is the windows loop that does that. For an external thread, you must write manually a loop. Imagine a situation without a loop and that you have a relative long running thread right ? and sudently you want to interrupt this thread to invoke your message and resume what it was doing ON THE SAME shared stack memory. This interruption would destroy your stack. This is simply NOT possible. The other possibility is to use a synchronization mechanism such as ManualResetEvent and just wait for a signal (a signal that comes outside your thread). So, to resume, in order to process a message from another thread, you basically have only two options:
1) You have a while loop, eventually using a little sleep (to give some time / ticks to other threads to do their job)
while (true) {
Thread.Sleep (5);
if (someMessageArrived) { ... }
}
2) You just wait for a message implementing somehow the producer / consummer architecture:
On listening thread:
aManualResetEvent.WaitOne ();
On the "producer" thread:
aManualResetEvent.Set ();
There are advanced classes in .NET framework that might help such as BlockingCollection.
Hope this helps
Assumming, that your class runs in its own thread that the only logic is to execute the incomming calls from other threads, this would be the solution:
(comments inside)
public class MyClass
{
private SomeObject object1;
private AnotherObject object2;
public MyClass()
{
object1 = new SomeObject();
object2 = new AnotherObject();
object1.AThreadedEvent += ThreadedEventHandler1;
object2.AnotherThreadedEvent += ThreadedEventHandler2;
}
// This runs in its own thread!
// Only add the real function call to the queue
public void ThreadedEventHandler1()
{
tasks.Add(ThreadedEventHandler1_really);
}
private void ThreadedEventHandler1_really()
{
// DO STUFF HERE
}
// This runs in its own thread!
// Only add the real function call to the queue
public void ThreadedEventHandler2()
{
tasks.Add(ThreadedEventHandler2_really);
}
// here is the actual logic of your function
private void ThreadedEventHandler2_really()
{
// DO STUFF HERE
}
// the queue of the tasks
BlockingCollection<Action> tasks = new BlockingCollection<Action>();
// this method never returns, it is blocked forever
// and the only purpose of i is to do the functions calls when they added to the queue
// it is done in the thread of this instance
public void StartConsume()
{
foreach (Action action in tasks.GetConsumingEnumerable())
{
// add logic before call
action();
// add logic after call
}
}
}
The solution based on that the caller threads tat calls the functions: ThreadedEventHandler1 and ThreadedEventHandler2, actually add the real call to the queue and emediately continue with their run.
From the other hand, StartConsume function iterates the queue and makes the calls of the added method calls. If you want to add another logic before and after call, you can add it in this function.
Hope it helped to achieve your goal.
Without completely understanding the rational behind your design. I can say that the problem you are trying to solve was solved many times before.
I will assume your main object is like a service which expects calls (in this case events) from itself and other services (the sub objects). If you would think about it in terms of services (which you arguably should) WCF solves that problem for you doing all the heavy lifting #Rami suggested.
You define the main service with the following behavior:
Instance Context Mode - Single
Concurrency Mode - Single
More about these here.
And every event handler would call that main service notifying it about the event.
I am pretty sure you would not go that far and implement every class as a service, but thought it is worth offering anyway as an option.
OK, based on all of your feedback (thanks!) I have a solution to my problem. The short answer: what I wanted to do isn't possible.
Here are more details for those who asked. I'm writing a DLL that manages a device attached to a serial port. This includes basic serial port COM (packet TX and RX, including parsing), and higher-level protocol behavior (TX, Ack, retry on timeout, etc.) The serial port event handlers provided by .NET are obviously asynchronous, as are the System.Timers.Timer objects that I use to handle timeouts, etc.
I am building the code around an MVVM architecture, so that my UI doesn't have any logic in it whatsoever. Hence my need to avoid exploiting Dispatcher or Invoke functionality provided by the UI.
What I was looking for was a way to handle asynchronous events within my DLL in the same simple manner provided by WinForms and WPF. But as has been pointed out, and as I learned when digging deeper, what you are *really doing when you call BeginInvoke or a Dispatcher is pushing something onto a queue, to be consumed later by a different thread polling the queue. Outside the context of a UI, no such polling architecture exists.
SO. My options are to lock the shared objects in my class to make it thread safe, or to implement my own polling architecture within another thread (to avoid blocking the program that uses the DLL) that emulates what the UI code already does.
In either case, the UI code will still need to use its Invoke or equivalent tools when handling events from the DLL class. I suppose that's OK.