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, string sessionId) { 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, sessionId); 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 int AddItem( int serverHandle, string itemDefinition) { ThrowIfDisposed(); int itemHandle = mxAccessServer.AddItem(serverHandle, itemDefinition); handleRegistry.RegisterItemHandle( serverHandle, itemHandle, itemDefinition, string.Empty, hasItemContext: false); return itemHandle; } public int AddItem2( int serverHandle, string itemDefinition, string itemContext) { ThrowIfDisposed(); int itemHandle = mxAccessServer.AddItem2(serverHandle, itemDefinition, itemContext); handleRegistry.RegisterItemHandle( serverHandle, itemHandle, itemDefinition, itemContext, hasItemContext: true); return itemHandle; } public void RemoveItem( int serverHandle, int itemHandle) { ThrowIfDisposed(); mxAccessServer.RemoveItem(serverHandle, itemHandle); handleRegistry.RemoveItemHandle(serverHandle, itemHandle); } public void Advise( int serverHandle, int itemHandle) { ThrowIfDisposed(); mxAccessServer.Advise(serverHandle, itemHandle); handleRegistry.RegisterAdviceHandle( serverHandle, itemHandle, MxAccessAdviceKind.Plain); } public void UnAdvise( int serverHandle, int itemHandle) { ThrowIfDisposed(); mxAccessServer.UnAdvise(serverHandle, itemHandle); handleRegistry.RemoveAdviceHandles(serverHandle, itemHandle); } public void AdviseSupervisory( int serverHandle, int itemHandle) { ThrowIfDisposed(); mxAccessServer.AdviseSupervisory(serverHandle, itemHandle); handleRegistry.RegisterAdviceHandle( serverHandle, itemHandle, MxAccessAdviceKind.Supervisory); } 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)); } } }