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;
}
}
}
Comments
Anonymous
March 05, 2007
I previously wrote about bi-directional remoting over IPC (see .Net Remoting continued and .Net RemotingAnonymous
May 11, 2007
The comment has been removedAnonymous
May 14, 2007
The comment has been removedAnonymous
July 02, 2007
The comment has been removedAnonymous
February 03, 2008
Hi Jack, I want to send a signal(some type of info) only once from process A to process B. untill signal arrived process B has to wait. Could you please tell me the simple way to do this in C# using IPC. it will really very helpful to me. Thanks, PareshAnonymous
February 04, 2008
Hi Paresh, I am making some assumptions because you didn't tell me whether A or B was the Client or Server. There are also other ways of implementing callbacks and/or event models. Referring to the sample code I posted, the Host app (i.e., “A”) has an object it expects clients (i.e., “B”) to implement. In this case, “RemoteObject”. When “A” calls the method, it “signals” (i.e., calls a method implemented by “B”) “B” which in turn will do some action based on “A” calling the Remote object. “B”’s implementation of RemoteMethod() waits until called/signaled before running any code.Anonymous
August 12, 2008
function ToggleDisplay (label, control) { if (control.style.display == "none") { control.style.displayAnonymous
August 12, 2008
I have published a sample on xternal process creation and coordination (http://blogs.msdn.com/jackg/archive/2008/08/11/process-creation-and-coordination.aspx).Anonymous
September 04, 2008
Great remoting example... Just to clarify something; HostApp is the 'server' and RemoteApp is the client or is it the other way around? Silly question isnt it? thanksAnonymous
September 08, 2008
Greg, In this example, the Host plays a dual role (i.e, Server and Client). Basically, Clients make calls to Servers. You can see that the Host registers both a client and server provider. - Jack