Hello, "Andrew"
I'm quite sure this is the case, but I'd like to consult to be sure:
If I have a UI which subscribes to a bunch of events which any of a number
of threads could trigger... and the UI's event handlers will update that UI's
controls, then the control updates _are_ done on the UI thread... correct?
All control updates must be done in UI thread. So if you have event handler that could invoked from another thread - you need to switch to UI thread through Control.Invoke method.
As another case you can create RealProxy wrapper and all method of MarshalByRefObject will be invoked on UI thread transparently:
class Program : MarshalByRefObject
{
static Program instance;
void Test()
{
Console.WriteLine(Thread.CurrentThread.Name);
}
static void Main(string[] args)
{
Thread.CurrentThread.Name = "Main Thread";
//all method's called through instance whil be done on main thread.
instance = (Program) ApartmentProxy.Marshal(new Program());
Thread thread = new Thread(new ThreadStart(ThreadStart));
thread.Start();
//this sample based on APC (asynchronous procedure call), so we need periodically to call Thread.Sleep(0) on working thread
Thread.Sleep(1000);
}
//Thread method
static void ThreadStart()
{
Thread.CurrentThread.Name = "Child Thread";
Console.WriteLine(Thread.CurrentThread.Name);
Console.WriteLine("Calling instance.Test");
// this method will be invoked on main thread
instance.Test();
}
}
//ApartmentProxy implementation
public class ApartmentProxy : RealProxy
{
delegate void ApcHandler(IntPtr param);
[DllImport("kernel32.dll")]
extern static IntPtr GetCurrentThreadId();
[DllImport("kernel32.dll")]
extern static bool QueueUserAPC(ApcHandler hadler, IntPtr thread, IntPtr param);
[DllImport("kernel32.dll")]
extern static IntPtr GetCurrentThread();
[DllImport("kernel32.dll")]
extern static IntPtr GetCurrentProcess();
[DllImport("kernel32.dll")]
extern static bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll")]
extern static bool DuplicateHandle(IntPtr sourceProcessHandle,
IntPtr sourceHandle, IntPtr targetProcessHandle,
out IntPtr targetHandle, uint desiredAccess, bool inheritHandle,
uint dwOptions);
private class ApartmentMessage
{
private ApartmentProxy _proxy;
private IMessage _source;
private IMessage _result;
private ApartmentMessage(ApartmentProxy proxy, IMessage source) {
_proxy = proxy;
_source = source;
}
private static void UserApcHandler(IntPtr param)
{
GCHandle messageHandle = (GCHandle)param;
ApartmentMessage This = (ApartmentMessage)messageHandle.Target;
try {
IMethodCallMessage mcm = This._source as IMethodCallMessage;
if (mcm != null) {
This._result = RemotingServices.ExecuteMessage(This._proxy.GetUnwrappedServer(), mcm);
}
else {
This._result = This._proxy.InitializeServerObject((IConstructionCallMessage) This._source);
}
}
finally {
This._proxy._event.Set();
}
}
public static IMessage ExecuteAppratmentMessage(ApartmentProxy proxy, IMessage msg)
{
ApartmentMessage appMessage = new ApartmentMessage(proxy, msg);
GCHandle messageHandle = GCHandle.Alloc(appMessage);
ApcHandler apcHandler = new ApcHandler(UserApcHandler);
bool bResult = QueueUserAPC(apcHandler, proxy._ownedThread, (IntPtr)messageHandle);
proxy._event.WaitOne();
GC.KeepAlive(apcHandler);
messageHandle.Free();
return appMessage._result;
}
}
internal IntPtr _ownedThread;
internal AutoResetEvent _event;
private ApartmentProxy(Type serverType)
: base (serverType)
{
IntPtr currentThread = GetCurrentThread();
IntPtr currentProcess = GetCurrentProcess();
DuplicateHandle(currentProcess, currentThread, currentProcess, out _ownedThread, 0, false, 2);
_event = new AutoResetEvent(false);
}
private ApartmentProxy(MarshalByRefObject mbr)
: this (mbr.GetType())
{
AttachServer(mbr);
}
~ApartmentProxy()
{
if (_ownedThread != IntPtr.Zero) {
CloseHandle(_ownedThread);
}
}
public override IMessage Invoke(IMessage msg)
{
IConstructionCallMessage ccm = msg as IConstructionCallMessage;
if (ccm != null) {
RealProxy.SetStubData(this, GetCurrentThreadId());
return InitializeServerObject(ccm);
}
lock (typeof(ApartmentMessage))
{
return ApartmentMessage.ExecuteAppratmentMessage(this, msg);
}
}
public static MarshalByRefObject Marshal(MarshalByRefObject obj)
{
return (MarshalByRefObject) new ApartmentProxy(obj).GetTransparentProxy();
}
}