using Implab.Components; using Implab.Diagnostics; using Implab.Parallels; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace Implab.Fx { public class StaApartment : RunnableComponent { readonly Thread m_worker; SynchronizationContext m_syncContext; SyncContextPromise m_enterPromise; readonly Promise m_threadStarted; readonly Promise m_threadTerminated; public StaApartment() : base(true) { m_threadStarted = new Promise(); m_threadTerminated = new Promise(); m_worker = new Thread(WorkerEntry); m_worker.SetApartmentState(ApartmentState.STA); m_worker.IsBackground = true; m_worker.Name = "STA managed aparment"; } public SynchronizationContext SyncContext { get { if (m_syncContext == null) throw new InvalidOperationException(); return m_syncContext; } } /// /// Returns the promise which will dispatch all handlers inside the apartment using it's /// /// /// Current implementation is optimized and will lost aync operation stack /// /// The promise public IPromise Enter() { if (m_enterPromise == null) throw new InvalidOperationException(); return m_enterPromise; } public IPromise Invoke(Action action) { Safe.ArgumentNotNull(action, "action"); if (m_syncContext == null) throw new InvalidOperationException(); var p = new Promise(); var lop = TraceContext.Instance.CurrentOperation; m_syncContext.Post(x => { TraceContext.Instance.EnterLogicalOperation(lop, false); try { if (p.CancelOperationIfRequested()) return; action(p); p.Resolve(); } catch (Exception e) { p.Reject(e); } finally { TraceContext.Instance.Leave(); } }, null); return p; } public IPromise Invoke(Func action) { Safe.ArgumentNotNull(action, "action"); if (m_syncContext == null) throw new InvalidOperationException(); var p = new Promise(); var lop = TraceContext.Instance.CurrentOperation; m_syncContext.Post(x => { TraceContext.Instance.EnterLogicalOperation(lop, false); try { if (p.CancelOperationIfRequested()) return; p.Resolve(action(p)); } catch (Exception e) { p.Reject(e); } finally { TraceContext.Instance.Leave(); } }, null); return p; } public IPromise Invoke(Action action) { Safe.ArgumentNotNull(action, "action"); if (m_syncContext == null) throw new InvalidOperationException(); var p = new Promise(); var lop = TraceContext.Instance.CurrentOperation; m_syncContext.Post(x => { TraceContext.Instance.EnterLogicalOperation(lop, false); try { if (p.CancelOperationIfRequested()) return; action(); p.Resolve(); } catch (Exception e) { p.Reject(e); } finally { TraceContext.Instance.Leave(); } }, null); return p; } public IPromise Invoke(Func action) { Safe.ArgumentNotNull(action, "action"); if (m_syncContext == null) throw new InvalidOperationException(); var p = new Promise(); var lop = TraceContext.Instance.CurrentOperation; m_syncContext.Post(x => { TraceContext.Instance.EnterLogicalOperation(lop, false); try { if (p.CancelOperationIfRequested()) return; p.Resolve(action()); } catch (Exception e) { p.Reject(e); } finally { TraceContext.Instance.Leave(); } }, null); return p; } /// /// Starts the apartment thread /// /// Promise which will be fullfiled when the syncronization /// context will be ready to accept tasks. protected override IPromise OnStart() { m_worker.Start(); return m_threadStarted; } /// /// Posts quit message to the message loop of the apartment /// /// Promise protected override IPromise OnStop() { m_syncContext.Post(x => Application.ExitThread(), null); return m_threadTerminated; } void WorkerEntry() { m_syncContext = new WindowsFormsSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(m_syncContext); m_enterPromise = new SyncContextPromise(m_syncContext); m_threadStarted.Resolve(); m_enterPromise.Resolve(); Application.OleRequired(); Application.Run(); try { OnShutdown(); m_threadTerminated.Resolve(); } catch(Exception err) { m_threadTerminated.Reject(err); } } /// /// Called from the STA apartment after the message loop is terminated, override this /// method to handle apartment cleanup. /// protected virtual void OnShutdown() { } protected override void Dispose(bool disposing) { if (disposing) { if (!m_threadTerminated.IsFulfilled) m_syncContext.Post(x => Application.ExitThread(), null); } base.Dispose(disposing); } } }