Code Red => .NET => Topic started by: Mike Leslie on April 01, 2019, 05:43:06 PM

Title: .NET remoting from application context? Add document from document context?
Post by: Mike Leslie on April 01, 2019, 05:43:06 PM
I have been trying to use .NET remoting to pass a Serializable data object from an Excel Add-in to an AutoCAD command.  This has been working decently well with my Excel Add-in exposing a MarshalByRefObject, then calling NetLoad and SendCommand to run my AutoCAD command and receive the serialized data at the other end.

What I have found is that when my AutoCAD command is running in a document context, I can use Activator.GetObject to get the exposed object, call methods on it, and get the required data out of it.  When my command is running in the application context (CommandFlags.Session), however, I can get the exposed object, but when I try to call one of its methods, I get a SocketException where it says that "No connection could be made because the target machine actively refused it".  I have confirmed that the sever is still running and available, it is just being blocked.  Normally I would expect this kind of error to be caused by a firewall or similar security setting, but there is no doubt that in this case, the error is exactly correlated to what context my command is running in.

The reason that I want to use application context is because my command should create a new drawing from a template file and then perform actions on that new drawing.  If I try to run DocumentCollectionExtension.Add(Core.Application.DocumentManager, DrawingSpecification.AutoCADTemplatePath) when this is in document context, I get a filer error but it works correctly in application context.

In short, the two requirements are conflicting.  I need to either connect to the remote socket when in application context or open a document when in document context.

 I have tried to use ExecuteInApplicationContext without the session attribute to run a document create function, but that will not open the document now, in the current command.  It queues the request and opens it after my command has completed.  I am not sure I would try to do the remote socket call from a document context when CommandFlags.Session.

Main Command:

    <CommandMethod("AutoDraw", CommandFlags.Session)>
    Public Sub CreateDrawingsFromRemoteData()

        Using dataChannel = New AutoDrawDataChannel
                For Each dwgSpec In dataChannel.GetDrawingSpecifications
                    Dim drawing = New GeneratedDrawing(dwgSpec)           'this constructor tries to create a new drawing from template using DocumentCollectionExtension.Add

                    'TODO: read drawing.Errors
            Catch ex As System.Exception
            End Try
        End Using
    End Sub

Wrapper around channel and remote object

Friend Class AutoDrawDataChannel
    Implements IDisposable

    Private _ServerObject As IAutoDrawDataProvider
    Private _Channel As TcpChannel

    Public Sub New()

        Dim IsRegistered As Boolean
            Dim clientProv = New BinaryClientFormatterSinkProvider
            Dim serverProv = New BinaryServerFormatterSinkProvider
            serverProv.TypeFilterLevel = TypeFilterLevel.Full
            Dim props As IDictionary = New Hashtable
            props("port") = 0
            'props("name") = privServiceName

            _Channel = New TcpChannel(props, clientProv, serverProv)
            ChannelServices.RegisterChannel(_Channel, True)
            IsRegistered = True
            _ServerObject = DirectCast(Activator.GetObject(GetType(IAutoDrawDataProvider), "tcp://localhost:8000/Autodraw"), IAutoDrawDataProvider)
        Catch ex As Exception
            If IsRegistered Then ChannelServices.UnregisterChannel(_Channel)
            Throw New RemotingException("Cannot connect to AutoDraw.Excel from AutoDraw.AutoCAD.", ex)
        End Try

        If _ServerObject Is Nothing Then
            Throw New RemotingException("Cannot connect to AutoDraw.Excel from AutoDraw.AutoCAD. Object returned is null.")
        End If
    End Sub

    Public Iterator Function GetDrawingSpecifications() As IEnumerable(Of DrawingSpecification)
        For Each dwgSpec In _ServerObject.GetDrawingSpecifications             ' crashes on this method call when CommandFlags.Session
            Yield dwgSpec
    End Function

    Private disposedValue As Boolean ' To detect redundant calls

    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not Me.disposedValue Then
        End If
        Me.disposedValue = True
    End Sub

    Protected Overrides Sub Finalize()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
    End Sub

End Class