using System; using System.Runtime.InteropServices; using Google.Protobuf.WellKnownTypes; using MxGateway.Contracts.Proto; namespace MxGateway.Worker.MxAccess; public sealed class MxAccessSession : IDisposable { private readonly object mxAccessComObject; private readonly IMxAccessServer mxAccessServer; private readonly IMxAccessEventSink eventSink; private readonly MxAccessHandleRegistry handleRegistry; private bool disposed; private MxAccessSession( object mxAccessComObject, IMxAccessServer mxAccessServer, IMxAccessEventSink eventSink, MxAccessHandleRegistry handleRegistry, int creationThreadId) { this.mxAccessComObject = mxAccessComObject ?? throw new ArgumentNullException(nameof(mxAccessComObject)); this.mxAccessServer = mxAccessServer ?? throw new ArgumentNullException(nameof(mxAccessServer)); this.eventSink = eventSink ?? throw new ArgumentNullException(nameof(eventSink)); this.handleRegistry = handleRegistry ?? throw new ArgumentNullException(nameof(handleRegistry)); CreationThreadId = creationThreadId; } public int CreationThreadId { get; } public MxAccessHandleRegistry HandleRegistry => handleRegistry; public WorkerReady CreateWorkerReady(int workerProcessId) { return new WorkerReady { WorkerProcessId = workerProcessId, MxaccessProgid = MxAccessInteropInfo.ProgId, MxaccessClsid = MxAccessInteropInfo.Clsid, ReadyTimestamp = Timestamp.FromDateTime(DateTime.UtcNow), }; } public static MxAccessSession Create( IMxAccessComObjectFactory factory, IMxAccessEventSink eventSink) { if (factory is null) { throw new ArgumentNullException(nameof(factory)); } if (eventSink is null) { throw new ArgumentNullException(nameof(eventSink)); } object? mxAccessComObject = null; try { mxAccessComObject = factory.Create(); if (mxAccessComObject is null) { throw new InvalidOperationException("MXAccess COM factory returned null."); } eventSink.Attach(mxAccessComObject); return new MxAccessSession( mxAccessComObject, new MxAccessComServer(mxAccessComObject), eventSink, new MxAccessHandleRegistry(), Environment.CurrentManagedThreadId); } catch (Exception exception) { eventSink.Detach(); if (mxAccessComObject is not null && Marshal.IsComObject(mxAccessComObject)) { Marshal.FinalReleaseComObject(mxAccessComObject); } throw MxAccessCreationException.From(exception); } } public int Register(string clientName) { ThrowIfDisposed(); int serverHandle = mxAccessServer.Register(clientName); handleRegistry.RegisterServerHandle(serverHandle, clientName); return serverHandle; } public void Unregister(int serverHandle) { ThrowIfDisposed(); mxAccessServer.Unregister(serverHandle); handleRegistry.UnregisterServerHandle(serverHandle); } public void Dispose() { if (disposed) { return; } eventSink.Detach(); if (Marshal.IsComObject(mxAccessComObject)) { Marshal.FinalReleaseComObject(mxAccessComObject); } disposed = true; } private void ThrowIfDisposed() { if (disposed) { throw new ObjectDisposedException(nameof(MxAccessSession)); } } }