using System; using System.Threading.Tasks; using System.Windows.Threading; namespace Laservall.Solidworks.Server { /// /// Marshals work to the SolidWorks main STA thread via the WPF Dispatcher. /// All SolidWorks COM API calls MUST go through this marshaller to avoid /// RPC_E_DISCONNECTED / DisconnectedContext errors. /// internal sealed class StaThreadMarshaller { private readonly Dispatcher _dispatcher; public StaThreadMarshaller(Dispatcher dispatcher) { _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); } /// /// Run an action synchronously on the STA thread. Blocks until complete. /// Use from background threads when you need the result immediately. /// public void Invoke(Action action) { if (_dispatcher.CheckAccess()) { action(); return; } _dispatcher.Invoke(action); } /// /// Run a function synchronously on the STA thread and return its result. /// Blocks the calling thread until complete. /// public T Invoke(Func func) { if (_dispatcher.CheckAccess()) { return func(); } return _dispatcher.Invoke(func); } /// /// Run a function asynchronously on the STA thread and return a Task with the result. /// Does not block the calling thread. /// public async Task InvokeAsync(Func func) { if (_dispatcher.CheckAccess()) { return func(); } return await _dispatcher.InvokeAsync(func); } /// /// Run an async function on the STA thread. The async continuations will /// remain on the STA thread (DispatcherSynchronizationContext), keeping /// all COM calls safe throughout the entire async chain. /// public Task InvokeAsync(Func> asyncFunc) { if (_dispatcher.CheckAccess()) { return asyncFunc(); } var tcs = new TaskCompletionSource(); _dispatcher.InvokeAsync(async () => { try { T result = await asyncFunc(); tcs.SetResult(result); } catch (Exception ex) { tcs.SetException(ex); } }); return tcs.Task; } /// /// Run an async action on the STA thread (no return value). /// public Task InvokeAsync(Func asyncFunc) { if (_dispatcher.CheckAccess()) { return asyncFunc(); } var tcs = new TaskCompletionSource(); _dispatcher.InvokeAsync(async () => { try { await asyncFunc(); tcs.SetResult(true); } catch (Exception ex) { tcs.SetException(ex); } }); return tcs.Task; } } }