Out of Process IPC/TCP Remoting code

Click this link for a description of the source code

 

I have attached the VS solutions in the file: SourceSolution.zip

View Source: Show Code ...

HostApp project – RemotingSamples Program.cs

using System;

using System.Collections.Generic;

using System.Text;

// RemotingServices

using System.Runtime.Remoting;

// BinaryServer/ClientFormatterSinkProvider(s)

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using System.Runtime.Remoting.Channels.Ipc;

// Need this for that pesky TypeFilter

using System.Runtime.Serialization.Formatters;

using System.Threading;

namespace RemotingSamples

{

    public class Host

    {

      // default to IPC channel but we can also handle TCP.

        // Just change the channelType = "tcp" or pass in a cmd line parm

        public static string channelType = "ipc";

        // The Dynamic and/or Private Ports are those in the range 49152–65535. These ports are not used by any defined application

        public static Int32 port = 65535;

        public static Uri objectUri;

        public static System.Collections.ArrayList arrayListServers = new System.Collections.ArrayList();

        // A Console Server sample App

        public static int Main(string[] args)

        {

            if (args != null && args.Length != 0 && args[0] != "")

                channelType = args[0].ToLower(); // (ipc, tcp)

            try

            {

                // Host Channel for remote object method calls

                // This Channel is also used for incomming calls (i.e., RemoteApp calls into objects in the Host App)

                IChannel ichannel = null;

                BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();

                BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();

                System.Collections.IDictionary props = new System.Collections.Hashtable();

                serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

                props["name"] = "Host";

                // ipc channel property, not used by tcp. Port is not used by ipc but it makes the uri easier to construct.

                props["portName"] = "localhost";

                // Surprise! We also need the Typefilter property. Don't ask me why, I just work here.

                props["typeFilterLevel"] = "Full";

                if (channelType == "tcp")

                { // Port is ignored by IPC

                    props["port"] = port;

                    ichannel = new TcpChannel(props, clientProvider, serverProvider);

                }

                else

                {

                    if (channelType == "ipc")

                    {

                        // When communicating cross proces between application domains on the same computer, the ipc channel is much faster than tcp

                        // Of course, in-process cross AppDomain communication does not need remoting channels

                        ichannel = new IpcChannel(props, clientProvider, serverProvider);

                    }

                }

                ChannelServices.RegisterChannel(ichannel, false);

                // Note that if you want to create multiple processes and you expect to have multiple remote AppDomains in

                // that process (see my example class RemoteADStub) then you will need need to come up with some scheme for

       // uniquely identifying tcp ports just like the need to create unique object identities like I did using GUIDs

                objectUri = GenerateUniqueObjectUri();

                CallServer();

                Console.WriteLine();

              Console.WriteLine("Press Enter to exit");

                Console.ReadLine();

                foreach (System.Diagnostics.Process RemoteProcess in arrayListServers)

                { RemoteProcess.Kill(); }

            } // try

            catch (Exception e)

            {

                Console.WriteLine(e.ToString());

                Console.WriteLine(e.InnerException);

            } // catch

            return 0;

        } // Main()

        internal static void CallServer()

        {

            try

            {

                System.Diagnostics.Process RemoteProcess;

                System.Diagnostics.Process CurrentProcess;

                CurrentProcess = System.Diagnostics.Process.GetCurrentProcess();

                String ServerName = @"RemoteApp.exe";

                string RemoteADChannel = "";

                string GUID = objectUri.AbsolutePath.Remove(0, 1);

                string cmdline = channelType + " " + port.ToString() + " " + GUID;

                // A scenerio where an object in the Host is called into from a remote process

                // This could be considered "two-way remoting"

                // The call into this object is done later but we created here before we create the remote process

                HostObject hostObject2 = new HostObject();

                RemotingServices.Marshal(hostObject2, GUID);

                // Start up a new process application

                RemoteProcess = System.Diagnostics.Process.Start(ServerName, cmdline);

                Console.WriteLine("Host PID: {0}, RemoteProcess PID: {1}\n, CmdLine: {2}", CurrentProcess.Id, RemoteProcess.Id, cmdline);

                #region ExternalProcessHack

                /* let the external process spin up or you get: system cannot find the file specified.

                Server stack trace:

                   at System.Runtime.Remoting.Channels.Ipc.IpcPort.Connect(String portName, Bool

                ean secure, TokenImpersonationLevel impersonationLevel, Int32 timeout)

                   at System.Runtime.Remoting.Channels.Ipc.ConnectionCache.GetConnection(String

                portName, Boolean secure, TokenImpersonationLevel level, Int32 timeout)

                   at System.Runtime.Remoting.Channels.Ipc.IpcClientTransportSink.ProcessMessage

                (IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, ITranspor

                tHeaders& responseHeaders, Stream& responseStream)

                   at System.Runtime.Remoting.Channels.BinaryClientFormatterSink.SyncProcessMess

                age(IMessage msg)

                */

                TimeSpan waitTime = new TimeSpan(0, 0, 1);

                Thread.Sleep(waitTime);

                #endregion

                arrayListServers.Add(RemoteProcess);

                System.Threading.Thread.CurrentThread.Join(100); // pump messages

                // A scenario where we use either tcp or ipc to call a remote process object in the remote default AppDomain

                RemoteObject remoteObject = (RemoteObject)Activator.GetObject(Type.GetType(@"RemotingSamples.RemoteObject,ClassLibrary1")

                    , objectUri.AbsoluteUri);

                if (remoteObject == null)

                    System.Console.WriteLine("Could not locate remote process object (RemoteObject)");

                else

                {

                    // A Scenerio where we create a local MBRO and pass it to the remote process object.

                    HostObject hostObject = new HostObject();

     // Call local instance of Host Object

                    hostObject.SomeMethod("HostApp");

                    // Pass the local object (MBRO) to a remote process object

                    // This remote object (i.e., it lives in a remote process default AppDomain (AD))

                    // will call a method on the object that was just instantiated in the local Host process

                    // This is the typical way of enabling object interaction between 2 App's accross a boundary

                    // sometimes refered to as two way remoting

                    remoteObject.RemoteMethod(hostObject);

                }

                // Another interesting scenario that is hard to work out.

                // Now let's call a remote object that resides in a remote process in a non-default AppDomain

                if (channelType == "ipc")

                    RemoteADChannel = "ipc://localhost:49152/" + GUID;

                if (channelType == "tcp")

                {

                    RemoteADChannel = "tcp://localhost:49152/" + GUID;

                }

                AnotherRemoteObject anotherRemoteObject = (AnotherRemoteObject)Activator.GetObject(Type.GetType(@"RemotingSamples.AnotherRemoteObject,ClassLibrary1")

                , RemoteADChannel);

                if (anotherRemoteObject == null)

                    System.Console.WriteLine("Could not locate remote process object (anotherRemoteObject)");

                else

                {

                    anotherRemoteObject.RemoteMethod();

                }

            } // try

            catch (Exception e)

            {

                Console.WriteLine(e.ToString());

                Console.WriteLine(e.InnerException);

            } // catch

        return;

        } // CallServer()

        internal static Uri GenerateUniqueObjectUri()

        {

            port--;

            string uriString = "";

            // Create a unique uri for this object

            if (channelType == "tcp")

                uriString = "tcp://localhost:" + port.ToString() + "/";

            if (channelType == "ipc")

                uriString = "ipc://localhost:" + port.ToString() + "/";

            Guid newGuid = Guid.NewGuid();

            uriString += newGuid.ToString();

            return new Uri(uriString);

        } // GenerateUniqueObjectUri()

    } // class Client

}

 

HostApp project – RemotingSamples ClassLibrary1 Class1.cs

 

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using System.Runtime.Remoting.Channels.Ipc;

using System.Runtime.Serialization.Formatters;

namespace RemotingSamples

{

    public class HostObject : MarshalByRefObject // A localy created Host Object

    {

        public HostObject() {}

        public void SomeMethod(string CalledFrom)

        {

            Console.WriteLine(

                "{0} activated in CurrentADName: {1}, CurrentADid: {2}, PID: {3}, ThreadId: {4}, Method: {5}, CalledFrom: {6}"

       , this.ToString()

                , AppDomain.CurrentDomain.FriendlyName

                , AppDomain.CurrentDomain.Id.ToString()

                , System.Diagnostics.Process.GetCurrentProcess().Id

                , System.Threading.Thread.CurrentThread.ManagedThreadId.ToString()

                , "SomeMethod()"

                , CalledFrom);

            return;

        }

    } // class HostObject

    public class RemoteObject : MarshalByRefObject // created in a remote process default AppDomain

    {

        public RemoteObject()

        {

            // Called on first method invocation not on Activate

            Console.WriteLine("{0} activated in CurrentADName: {1}, CurrentADid: {2}, PID: {3}, ThreadId: {4}"

                 , this.ToString()

                 , AppDomain.CurrentDomain.FriendlyName

                 , AppDomain.CurrentDomain.Id.ToString()

                 , System.Diagnostics.Process.GetCurrentProcess().Id

                 , System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

        }

        public void RemoteMethod (object hostObject) // Pass in a localy created Host object

        {

            // Look Mom, no hands. i.e., No need for the remote process to activate an object in the Host.

            // Once the channel is established and we have a remote object that has been remoted over the channel

            // we can just pass a local MBRO through to the remote process object for it to call into.

            // Some consider this two way remoting. I like the pattern.

            ((HostObject)hostObject).SomeMethod("RemoteObject.RemoteMethod"); // Call a method on the remoted local Host object

        } // RemoteMethod (object hostObject)

        public void RemoteStubInNewAD(string channelType, int port, string UniqueObjIdentifier)

        {

            // Create a new AppDomain in a remote process

            AppDomain appDomain = AppDomain.CreateDomain("NewAppDomain");

            // Create an object in the new AppDomain

            RemoteADStub remoteADStub =

                (RemoteADStub)appDomain.CreateInstanceAndUnwrap(typeof(RemoteADStub).Assembly.FullName, typeof(RemoteADStub).FullName);

            // Use the object created in the new AppDomain to register a channel for this new AppDomain

            remoteADStub.CreateChannel(channelType, port, UniqueObjIdentifier);

        }

    } // class RemoteObject

    public class RemoteADStub : MarshalByRefObject

    {

        public void CreateChannel(string channelType, int port, string UniqueObjIdentifier) // Each remote AD needs a channel

        {

            // Must Create channel in new AppDomain

            IChannel ichannel = null;

            BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();

            BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();

            System.Collections.IDictionary props = new System.Collections.Hashtable();

            props["name"] = "NewAD";

            // IPC required property, port (arg[1]) is only used for uniqueness

            props["portName"] = "localhost:" + port.ToString();

            props["typeFilterLevel"] = "Full";

            serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

            if (channelType == "tcp")

            {

                props["port"] = port;

                ichannel = new TcpChannel(props, clientProvider, serverProvider);

            }

            if (channelType == "ipc")

                ichannel = new IpcChannel(props, clientProvider, serverProvider);

            ChannelServices.RegisterChannel(ichannel, false);

            // Server creates instance of object to remote

            AnotherRemoteObject anotherRemoteObject = new AnotherRemoteObject();

            // Register remotable object with Remoting service and unique Object Id

            RemotingServices.Marshal(anotherRemoteObject, UniqueObjIdentifier);

        } // CreateChannel

    } // class RemoteADStub

    public class AnotherRemoteObject : MarshalByRefObject // created in a remote process default AppDomain

    {

        public AnotherRemoteObject()

        {}

        public void RemoteMethod()

        {

             Console.WriteLine("{0} activated in CurrentADName: {1}, CurrentADid: {2}, PID: {3}, ThreadId: {4}"

             , this.ToString()

             , AppDomain.CurrentDomain.FriendlyName

             , AppDomain.CurrentDomain.Id.ToString()

             , System.Diagnostics.Process.GetCurrentProcess().Id

             , System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

        }

    }

   

}

RemoteApp project – RemotingSamples RemoteApp Program.cs

 

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Serialization.Formatters;

using System.Runtime.Remoting.Channels.Tcp;

using System.Runtime.Remoting.Channels.Ipc;

using System.Security.Permissions;

using System.Collections;

using System.Threading;

// JackG 02/15/2007

// Activation may be done via RemotingServices.Marshal

// or

// RemotingConfiguration.Configure(@"C:\blah\Client.exe.config", false); // works!

// or

// RemotingConfiguration.RegisterWellKnownServiceType // I had problems with this method

namespace RemotingSamples

{

    public class Sample

    {

        public static int Main(string[] args)

        {

            IChannel ichannel = null;

            string channelType = "tcp"; // default to tcp. IPC (i.e., Named pipes also supported

            Int32 port = -1; // default to invalid port

            string uniqueObj = "GUID";

            if (args[0] != "")

          channelType = args[0];

            if (args[1] != "")

                port = Convert.ToInt32(args[1]);

            if (args[2] != "")

                uniqueObj = args[2];

            try

            {

                string hostURL = "";

                Console.WriteLine("{0} activated\nCurrentADName: {1}, CurrentADid: {2}, PID: {3}, ThreadId: {4}"

                 , "RemotingApp"

                 , AppDomain.CurrentDomain.FriendlyName

                 , AppDomain.CurrentDomain.Id.ToString()

                 , System.Diagnostics.Process.GetCurrentProcess().Id

                 , System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

                Console.WriteLine("cmdline: {0}, {1}, {2}", args[0], args[1], args[2]);

                BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();

                BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();

                // Security breaking change. Needed to enable the client to act as a server.

                serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

                System.Collections.IDictionary props = new System.Collections.Hashtable();

                props["name"] = "Remote";

                // IPC required property. port (arg[1]) is only used for uniqueness

                props["portName"] = "localhost:" + args[1];

                props["typeFilterLevel"] = "Full";

                if (channelType == "tcp")

                {

                    // ipc does not need port

                    props["port"] = port;

                    ichannel = new TcpChannel(props, clientProvider, serverProvider);

                    // The URL of the Host channel/UniqueId of Host object your calling

                    hostURL = @"tcp://localhost:65535/" + uniqueObj;

                }

                if (channelType == "ipc")

                {

                    ichannel = new IpcChannel(props, clientProvider, serverProvider);

                    // The URL of the Host channel/UniqueId of Host object your calling

                    hostURL = @"ipc://localhost/" + uniqueObj;

                }

                ChannelServices.RegisterChannel(ichannel, false);

                // Create instance of object to remote

                RemoteObject remoteObject = new RemoteObject();

                // Register remotable object with Remoting service with a unique Object Id

                RemotingServices.Marshal(remoteObject, uniqueObj);

                // Now create an object in another AppDomain that a Host app can call

                remoteObject.RemoteStubInNewAD(channelType, 49152, uniqueObj);

                // Get the type info in order to Call from RemoteApp to HostApp

                HostObject hostObject = (HostObject)Activator.GetObject(Type.GetType(@"RemotingSamples.HostObject,ClassLibrary1")

                , hostURL);

                if (hostObject == null)

                  Console.WriteLine("Could not locate remote process object (HostObject)");

                else

                {

                    // Call a method on the Host App object over the remoting channel

                    hostObject.SomeMethod("RemoteApp");

                }

                TimeSpan waitTime = new TimeSpan(0, 0, 5);

                // keep the process alive

                while (1 == 1)

                {

                    // pump messages

                    Thread.CurrentThread.Join(waitTime);

                    Thread.Sleep(waitTime);

                }

            }

            catch (Exception e)

            {

                Console.WriteLine(e.ToString());

                Console.WriteLine(e.InnerException);

                Console.ReadLine();

            }

            return 0;

        }

    }

}

 

SourceSolution.zip