Results 1 to 12 of 12

Thread: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

  1. #1

    Thread Starter
    Super Moderator FunkyDexter's Avatar
    Join Date
    Apr 2005
    Location
    An obscure body in the SK system. The inhabitants call it Earth
    Posts
    7,900

    Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    I'm rewriting an old application that used .Net Remoting to control a second application. The rewrite involves changing from VB to C# (so apologies for the mixed languages in this post) and targeting core rather than a framework. Core doesn't support .Net Remoting and the advise seems to be to use Anonymous Pipes. I'm not familiar with either of these so it's a bit of a baptism of fire. I've been following the example here and have got it sending a simple string from one app to the other.

    Here's my server app code:-
    c# Code:
    1. public void StartAquatorXV(string exePath, int port)
    2.         {
    3.             mAquatorXVPipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);
    4.             ProcessStartInfo psi = new ProcessStartInfo(exePath, $"/anonymousPipe:{mAquatorXVPipe.GetClientHandleAsString()}");
    5.             psi.UseShellExecute = false;
    6.             mProcess = Process.Start(psi);
    7.             mAquatorXVPipe.DisposeLocalCopyOfClientHandle();
    8.             mProcess.WaitForInputIdle();
    9.             mAquatorXVWriter = new StreamWriter(mAquatorXVPipe);
    10.             mAquatorXVWriter.AutoFlush = true;
    11.             mAquatorXVWriter.WriteLine("SYNC");
    12.             mAquatorXVPipe.WaitForPipeDrain();
    13.  
    14.  
    15.             mAquatorXVWriter.WriteLine("Test");
    16.         }

    Here's the client app code:-
    C# Code:
    1. PipeStream mAquatorServerPipe;
    2.         StreamReader mAquatorServerReader;
    3.         public static void ListenForAquatorServer(StreamReader reader)
    4.         {
    5.             string input;
    6.             while ((input = reader.ReadLine()) != null)
    7.             {
    8.                 Console.WriteLine(input);
    9.             }
    10.         }
    11.         public void SetUpClientPipe(string serverHandle)
    12.         {
    13.             mAquatorServerPipe = new AnonymousPipeClientStream(PipeDirection.In, serverHandle);
    14.             mAquatorServerReader = new StreamReader(mAquatorServerPipe);
    15.             string input;
    16.             do
    17.             {
    18.                 input = mAquatorServerReader.ReadLine();
    19.             }
    20.             while (!input.StartsWith("SYNC"));
    21.  
    22.             Thread listener = new Thread(() => ListenForAquatorServer(mAquatorServerReader));
    23.             listener.Start();
    24.         }

    Note, all the examples I could find dispose of the pipes and streams as soon as they're used which doesn't work for me, I need the client app to continue running but be able to interrupt it to send messages so I'm keeping those objects alive and will explicitly dispose of them on shutdown.

    My next problem is that I'd like to be able to communicate with the client app in a strongly typed fashion. The original app used to do this to get an interface to work against:-
    VB.Net Code:
    1. ' Start AquatorXV, tell it to listen on the designated port, and wait for it to be ready
    2.             Dim psi As ProcessStartInfo = New ProcessStartInfo(location, $"/remoting:{port}")
    3.             psi.UseShellExecute = False
    4.             m_process = Process.Start(psi)
    5.             m_process.WaitForInputIdle()
    6.  
    7.             ' Create the one remote AquatorXVInterfaces object
    8.             If m_channel Is Nothing Then
    9.                 m_channel = New TcpChannel()
    10.                 ChannelServices.RegisterChannel(m_channel, True)
    11.             End If
    12.             Dim url As String = $"tcp://localhost:{port}/OSS.AquatorXV.Server"
    13.             Dim o As Object = Activator.GetObject(Type.GetType("AquatorXVInterfaces.IServer, AquatorXVInterfaces"), url)  '<<<This is the bit I need to mimic
    14.             m_aquatorXV = CType(o, AquatorXVInterfaces.IServer)

    So if I understand that right it uses Activator.GetObject to retrieve the client application from the uri and then cast it to a known interface to get strong typing. I'm looking for an equivalent of that which will work in Core using Anonymous Pipes. Is that possible or am I going to have to couch all the communication in untyped strings?
    The best argument against democracy is a five minute conversation with the average voter - Winston Churchill

    Hadoop actually sounds more like the way they greet each other in Yorkshire - Inferrd

  2. #2
    PowerPoster PlausiblyDamp's Avatar
    Join Date
    Dec 2016
    Location
    Pontypool, Wales
    Posts
    2,458

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    Quote Originally Posted by FunkyDexter View Post
    I'm rewriting an old application that used .Net Remoting to control a second application. The rewrite involves changing from VB to C# (so apologies for the mixed languages in this post) and targeting core rather than a framework. Core doesn't support .Net Remoting and the advise seems to be to use Anonymous Pipes. I'm not familiar with either of these so it's a bit of a baptism of fire. I've been following the example here and have got it sending a simple string from one app to the other.

    Here's my server app code:-
    c# Code:
    1. public void StartAquatorXV(string exePath, int port)
    2.         {
    3.             mAquatorXVPipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);
    4.             ProcessStartInfo psi = new ProcessStartInfo(exePath, $"/anonymousPipe:{mAquatorXVPipe.GetClientHandleAsString()}");
    5.             psi.UseShellExecute = false;
    6.             mProcess = Process.Start(psi);
    7.             mAquatorXVPipe.DisposeLocalCopyOfClientHandle();
    8.             mProcess.WaitForInputIdle();
    9.             mAquatorXVWriter = new StreamWriter(mAquatorXVPipe);
    10.             mAquatorXVWriter.AutoFlush = true;
    11.             mAquatorXVWriter.WriteLine("SYNC");
    12.             mAquatorXVPipe.WaitForPipeDrain();
    13.  
    14.  
    15.             mAquatorXVWriter.WriteLine("Test");
    16.         }

    Here's the client app code:-
    C# Code:
    1. PipeStream mAquatorServerPipe;
    2.         StreamReader mAquatorServerReader;
    3.         public static void ListenForAquatorServer(StreamReader reader)
    4.         {
    5.             string input;
    6.             while ((input = reader.ReadLine()) != null)
    7.             {
    8.                 Console.WriteLine(input);
    9.             }
    10.         }
    11.         public void SetUpClientPipe(string serverHandle)
    12.         {
    13.             mAquatorServerPipe = new AnonymousPipeClientStream(PipeDirection.In, serverHandle);
    14.             mAquatorServerReader = new StreamReader(mAquatorServerPipe);
    15.             string input;
    16.             do
    17.             {
    18.                 input = mAquatorServerReader.ReadLine();
    19.             }
    20.             while (!input.StartsWith("SYNC"));
    21.  
    22.             Thread listener = new Thread(() => ListenForAquatorServer(mAquatorServerReader));
    23.             listener.Start();
    24.         }

    Note, all the examples I could find dispose of the pipes and streams as soon as they're used which doesn't work for me, I need the client app to continue running but be able to interrupt it to send messages so I'm keeping those objects alive and will explicitly dispose of them on shutdown.

    My next problem is that I'd like to be able to communicate with the client app in a strongly typed fashion. The original app used to do this to get an interface to work against:-
    VB.Net Code:
    1. ' Start AquatorXV, tell it to listen on the designated port, and wait for it to be ready
    2.             Dim psi As ProcessStartInfo = New ProcessStartInfo(location, $"/remoting:{port}")
    3.             psi.UseShellExecute = False
    4.             m_process = Process.Start(psi)
    5.             m_process.WaitForInputIdle()
    6.  
    7.             ' Create the one remote AquatorXVInterfaces object
    8.             If m_channel Is Nothing Then
    9.                 m_channel = New TcpChannel()
    10.                 ChannelServices.RegisterChannel(m_channel, True)
    11.             End If
    12.             Dim url As String = $"tcp://localhost:{port}/OSS.AquatorXV.Server"
    13.             Dim o As Object = Activator.GetObject(Type.GetType("AquatorXVInterfaces.IServer, AquatorXVInterfaces"), url)  '<<<This is the bit I need to mimic
    14.             m_aquatorXV = CType(o, AquatorXVInterfaces.IServer)

    So if I understand that right it uses Activator.GetObject to retrieve the client application from the uri and then cast it to a known interface to get strong typing. I'm looking for an equivalent of that which will work in Core using Anonymous Pipes. Is that possible or am I going to have to couch all the communication in untyped strings?
    Perhaps this isn't directly helping but... If you haven't invested too much time in this already grpc might be a better option.

    https://docs.microsoft.com/en-us/asp...aspnetcore-6.0 is a decent starting point.
    Last edited by PlausiblyDamp; May 5th, 2022 at 08:42 AM.

  3. #3
    MS SQL Powerposter szlamany's Avatar
    Join Date
    Mar 2004
    Location
    Connecticut
    Posts
    18,263

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    Not sure if this will be of any assistance...

    App I wrote a while ago had 4 services running listening on HTTPListener's - that's besides the point.

    For transmitting data, I created a CLASS, made it SERIALIZABLE and implemented ICloneable.

    I have a serializer class that turns this other "CLASS" into a memory stream with a BinaryFormatter.

    I can share code if this is in anyway connected with what you are asking, lol!

    *** Read the sticky in the DB forum about how to get your question answered quickly!! ***

    Please remember to rate posts! Rate any post you find helpful - even in old threads! Use the link to the left - "Rate this Post".

    Some Informative Links:
    [ SQL Rules to Live By ] [ Reserved SQL keywords ] [ When to use INDEX HINTS! ] [ Passing Multi-item Parameters to STORED PROCEDURES ]
    [ Solution to non-domain Windows Authentication ] [ Crazy things we do to shrink log files ] [ SQL 2005 Features ] [ Loading Pictures from DB ]

    MS MVP 2006, 2007, 2008

  4. #4

    Thread Starter
    Super Moderator FunkyDexter's Avatar
    Join Date
    Apr 2005
    Location
    An obscure body in the SK system. The inhabitants call it Earth
    Posts
    7,900

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    @szlamany, that sounds roughly like what I'm currently considering for the actual data as I think that the pipes are only capable of transmitting text. So I guess I'll have to serialize the parameters of any calls before writing to the stream and then deserialise on the other side. Similarly with responses but in reverse. I'm pretty confident I can work that out myself but if you've got any code I can use as a template I'd definitely appreciate it.

    My concern here, though, is how I expose and police the API the client offers. I.e. how does the server know what methods are available, what their signatures are etc. Ideally I want to do it in a way that's strongly typed so I can catch any errors at compile time rather than run time. I'm hoping there's something built into core that will assist me with this but, if not, I can probably come up with something myself. As a fall back I can add a RESTful API to the client app and use that but I'd rather something strongly typed.

    @PlausiblyDamp, I had seen people mention gRPC when I was investigating what the replacement for remoting was but didn't really take it on board as I came across Microsoft recommending Anonymous Pipes. A quick look at that link is intriguing. I think I'll spend some time digesting that today and might switch if it looks like it'll do what I want. Thanks.
    The best argument against democracy is a five minute conversation with the average voter - Winston Churchill

    Hadoop actually sounds more like the way they greet each other in Yorkshire - Inferrd

  5. #5
    PowerPoster PlausiblyDamp's Avatar
    Join Date
    Dec 2016
    Location
    Pontypool, Wales
    Posts
    2,458

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    Quote Originally Posted by FunkyDexter View Post
    @szlamany, that sounds roughly like what I'm currently considering for the actual data as I think that the pipes are only capable of transmitting text. So I guess I'll have to serialize the parameters of any calls before writing to the stream and then deserialise on the other side. Similarly with responses but in reverse. I'm pretty confident I can work that out myself but if you've got any code I can use as a template I'd definitely appreciate it.

    My concern here, though, is how I expose and police the API the client offers. I.e. how does the server know what methods are available, what their signatures are etc. Ideally I want to do it in a way that's strongly typed so I can catch any errors at compile time rather than run time. I'm hoping there's something built into core that will assist me with this but, if not, I can probably come up with something myself. As a fall back I can add a RESTful API to the client app and use that but I'd rather something strongly typed.

    @PlausiblyDamp, I had seen people mention gRPC when I was investigating what the replacement for remoting was but didn't really take it on board as I came across Microsoft recommending Anonymous Pipes. A quick look at that link is intriguing. I think I'll spend some time digesting that today and might switch if it looks like it'll do what I want. Thanks.
    I have only had a quick play with gRPC but it does try to enforce the ideas of interfaces and DTOs quite well, the link does show how you can build strongly typed objects etc. pretty similar in some respects to how WCF used to do things (but without all the junk that came with WCF)

  6. #6
    Fanatic Member
    Join Date
    Jun 2019
    Posts
    557

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    I am using NATS as pub-sub server to create "control plane" for multiple apps on single computer or inside network(s). You can serialize your strictly defined classes and pass the message to listeners. The message is transferred as byte array so you can use your choice of serializing library to JSON, protobuf or MessagePack format.

    NATS is multiplatform pub-sub server and is distributed as really small single executable. There are client libraries for many languages, including .NET platform. The libraries are generic but you can add small library on top of it to automate (de)serializing and manage the boilerplate code.

    Example server definition with my simple library that works on top of NATS:
    C# pseudocode Code:
    1. var host = NatsHost.CreateBuilder()
    2.   .AddServer("server1_ip")
    3.   .AddServer("server2_ip")
    4.   .UseSecureConnection()
    5.   .Build();
    6. var pubsub = host.Connect();
    7.  
    8. // Sample replier - receives RequestClass and returns ResponseClass object. Generates some response using request data
    9. pubsub.AddReplier<RequestClass, ResponseClass>("service.comm.channel", "workqueue", req =>
    10. {
    11.   Console.WriteLine(req.SomeField);
    12.   return GenerateResponse(req);
    13. });
    14.  
    15. // Replier that don't receive request but only returns response - this one returns server time
    16. // The service is listening on channel Services.System.GetServerTime
    17. string serverName = "tech01";
    18. pubsub.AddReplier<DateTime>($"Services.System.GetServerTime.{serverName}",
    19. () => DateTime.UtcNow());

    To consume the "pubsub" service my lib has also simplified methods:
    C# Code:
    1. // Query the first example service
    2. var request = new RequestClass { Field1-"some data", Field2 = 123 };
    3. ResponseClass response = pubsub.RequestReply("service.comm.channel", request);
    4.  
    5. // Get server time
    6. DateTime serverTime = pubsub.RequestReply<DateTime>($"Services.System.GetServerTime.tech01");
    7.  
    8. pubsub.Publish("Servers.Control.Command.Update.*", new UpdateCommand { Version="1.2.3",
    9. DownloadURL="https://storage/files/services/MyService/updates/v1.2.3/MyService.v1.2.3.zip });

    With proper design of listening channels for services you can design the communication between apps. Some services don't return response so you can just send (publish) message that they will receive - for example you can send "update app to ver 1.2.3" command so the app will download specified version and autoupdate itself.

    Finally, one important difference from other approaches is that you don't have to configure apps to know other apps IPs. They must know only server(s) addresses. The pub-sub server do the job to redirect the messages to the proper listener(s). Also NATS can be configured in cluster mode so even single instance is stopped for some reason (server down, network split, NATS crashed), the rest of the cluster will serve the requests so your system will be much more resilient.
    Last edited by peterst; May 5th, 2022 at 12:51 PM.

  7. #7

    Thread Starter
    Super Moderator FunkyDexter's Avatar
    Join Date
    Apr 2005
    Location
    An obscure body in the SK system. The inhabitants call it Earth
    Posts
    7,900

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    Gah! I was sold on gRPC and I've been trying it out today but I don't think it's going to work for me. The app being controlled would need to be converted to a gRPC server but it can't be because it's targetting a traditional .Net framework rather than core. I'm writing the controlling app (which would be the client in this scenario) to target core but I don't have freedom to change the target of the controlled app.

    I thought about adding the server as a separate project in the same solution but that doesn't work either. The server project would need a reference to the controlled project to do it's work but the controlled app also needs a reference to the server project to start up the server so circular reference.

    I think I'll post in their community before I give up though.

    @peterst, I'm taking a look at the NATS site. I can see it handles the request response process but does it provide a typesafe experience or does it require the requests and responses being correctly formed by hand? Looking at their docs it looks like you simply pass a string or set of strings to a request and get a string back as a reply. That's not really what I'm after. I want to be able to pass complex types and have the type checking and serialisation concerns abstracted away. Otherwise I'll just use the anonymous pipes and go RESTful.
    The best argument against democracy is a five minute conversation with the average voter - Winston Churchill

    Hadoop actually sounds more like the way they greet each other in Yorkshire - Inferrd

  8. #8
    Fanatic Member
    Join Date
    Jun 2019
    Posts
    557

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    I've created small library on top of NATS to do the type specific conversion. The same way ASPNET Core handles http requests with type specific parameters - invisible for the developer. gRPC also works the same under the hood.

    If you directly use NATS library, you work with byte arrays. The same way you will work with the Context.Request.Body in aspnet core. But having higher level support of specific data types in library, you just don't care how the library handle them. You work with strong data types - just check the examples in my previous post. You may find them pretty similar to minimal web API in .NET Core 6, as my new version of the library is evolving during the years and I really like that way of defining services. Add the service and pass lambda of the handler. Put the parameter types in Action(Of TRequest) or Func(Of TRequest, TResponse) and the compiler will do the strict type checks for you.

    Here is definition from my library of method to add replier - function that takes request and returns response (replies to message):
    C# Code:
    1. public ChannelListener AddReplier<TRequest, TResponse>(string channel, Func<TRequest, TResponse> subscriber)
    2. {
    3.     var listener = new ChannelListener(channel, _conn.SubscribeAsync(channel, (s, e) =>
    4.     {
    5.         TResponse reply = subscriber.Invoke(_serialzier.Deserialize<TRequest>(e.Message.Data));
    6.         var serializedReply = _serialzier.Serialize(reply);
    7.         PublishMessage(e.Message.Reply, serializedReply);
    8.     }));
    9.        
    10.     _listeners.Add(listener.Guid, listener);
    11.     return listener;
    12. }

  9. #9
    Fanatic Member
    Join Date
    Jun 2019
    Posts
    557

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    I forgot to mention another advantage of pub-sub pattern. You open ports only on server. Clients connect to server and don't need to open ports locally. Zero configuration required to "call" (send messages) to listeners as they can be everywhere in the network even with changing IP addresses (DHCP or just new machines with new IPs). If you want to identify specific listener - define proper channel name with specific listener name inside - this is just one of the access patterns for pub-sub messaging.

    In another thread here someone asked how to ping multiple servers. I proposed to do the opposite - each server to "ping" the listener using pub-sub messaging. This way the developer will not care if there are 1, 5 or 3000 servers in the network and where they are located (it could be NATS edge connections between multiple data centers). The listener will receive pings, mark servers down when ping was not received for specified amount of time and perform some actions.

    Also on server (as hardware machine) the app (that sends the pings) can collect local for the OS data without exposing opened ports or when that is impossible - UPS status info, SQL server status, CPU and memory usage, free disk space, etc. Collect the info and send it regularly to the listeners which will aggregate into centralized database so admins can watch what happens with the machines. Also use these messages instead of pings. Or do whatever you need.

    But using messaging requires re-thinking of apps structure and how the communication is performed. Just imagine it like room with people where you can tell (send message) to someone to do something or to everyone. Ask for information and get response. Ask everyone and get multiple responses. Or just listen what others say.

  10. #10

    Thread Starter
    Super Moderator FunkyDexter's Avatar
    Join Date
    Apr 2005
    Location
    An obscure body in the SK system. The inhabitants call it Earth
    Posts
    7,900

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    gRPC ended up being a dead end for the reasons given above. I've decided to cut my losses, fall back on targeting the framework rather than core and switch to using WCF. However, I'm still running in to a problem (the client is failing to connect to the end point - I suspect the endpoint may be being set up and immediately dropped).

    As the topic of my question has substantially changed I'm going to post a new question and link to it from here.

    Edit>Scratch that, I spotted the problem just after posting.
    Last edited by FunkyDexter; May 12th, 2022 at 07:45 AM.
    The best argument against democracy is a five minute conversation with the average voter - Winston Churchill

    Hadoop actually sounds more like the way they greet each other in Yorkshire - Inferrd

  11. #11
    PowerPoster PlausiblyDamp's Avatar
    Join Date
    Dec 2016
    Location
    Pontypool, Wales
    Posts
    2,458

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    Quote Originally Posted by FunkyDexter View Post
    gRPC ended up being a dead end for the reasons given above. I've decided to cut my losses, fall back on targeting the framework rather than core and switch to using WCF. However, I'm still running in to a problem (the client is failing to connect to the end point - I suspect the endpoint may be being set up and immediately dropped).

    As the topic of my question has substantially changed I'm going to post a new question and link to it from here.

    Edit>Scratch that, I spotted the problem just after posting.
    https://github.com/CoreWCF/CoreWCF might be an option if you are wanting to move to core, not tried it but from a few articles I have read it seems a decent port of WCF to core.

  12. #12

    Thread Starter
    Super Moderator FunkyDexter's Avatar
    Join Date
    Apr 2005
    Location
    An obscure body in the SK system. The inhabitants call it Earth
    Posts
    7,900

    Re: Moving from .Net Remoting to Anonymous Pipes, equivalent of Activator.GetObject

    Thanks for the suggestion but I'll probably pass. 2 Reasons really:-
    1. I don't really need to move to core. As I was approaching this tool as a new project it seemed sensible to target it but the truth is that the controlled application is already targeting a framework so is kinda locked to Windows anyway. If that changes we'd need to revisit both apps any way.
    2. The way I want to use it is a bit non-standard (i.e. it needs to self host the service and the client can't have a web reference at design time). I don't know if a third party lib will support that and I notice that the docs and walkthroughs don't seem to cover it so it'll be an ache to work out.

    Using WCF seems to be working great for now so I think I've reached a comfortable happy ground.

    Thanks to everyone for the suggestions.
    The best argument against democracy is a five minute conversation with the average voter - Winston Churchill

    Hadoop actually sounds more like the way they greet each other in Yorkshire - Inferrd

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width