I recently had to take a piece of legacy code that would only work when run in the x86 .NET CLR and make it talk to a piece of legacy code that contained 64-bit native logic. This, of course, meant that I had to run 64-bit code from within a 32-bit CLR, something that is difficult to do.

The Solution

The solution I ended up going with was to use .NET remoting. While it’s not a sexy new piece of .NET it’s a lot cleaner than COM ever was and should be sufficient for our needs.

Create three projects: Server (x64), Client (x86), and Contracts (Any CPU). From there we’ll write some simple code in each.

We’ll start, as always, by defining the domain objects. Create an interface in Contracts, and an implementation of that interface in Server.

Contracts’ ITester.cs:

[...]
public interface ITester {
	void Test();
}
[...]

Server’s Tester.cs:

[...]
public class Tester : MarshalByRefObject, ITester {
	void Test() {
		if(IntPtr.Size != 8) { throw Exception("Wasn't being run on a 64-bit machine!"); }
	}
}
[...]

Now that we’ve had the main actors of our service built, let’s start working on having the server advertise it and tell it where to find the concrete object for instantiation.

Add Contracts as a reference to Server, and proceed with writing some code.

Server’s Program.cs:

[...]
static void Main(string[] args) {
	var serviceEntry = new WellKnownServiceEntry(typeof(Tester), "Tester", WellKnownObjectMode.SingleCall);
	RemotingConfiguration.RegisterWellKnownServiceType(serviceEntry);

	// Set up TCP server. Note, second argument avoids guaranteeing security
	// (see https://msdn.microsoft.com/en-us/library/ms223155%28v=vs.110%29.aspx )
	ChannelServices.RegisterChannel(new TcpServerChannel(9999), false);

	// Wait
	Console.WriteLine("Press any key to quit server.");
	Console.ReadLine();
}
[...]

Add Contracts as a project reference to Client, and now write the code that will call this.

Client’s Program.cs:

[...]
static void Main(string[] args) {
	var remoteTester = (ITester)Activator.GetObject(typeof(ITester), "tcp://localhost:9999/Tester");
	remoteTester.Test();
}
[...]

Now build everything. Run the Server and then the Client. You should see something appear in the server’s console output when the Tester object is run remotely.

Caveats

Notice that this example code takes many shortcuts which may not be appropriate for your project. In particular, the ensureSecurity flag is set to false, which may expose the inner workings of your application to snoopers on a misconfigured server. Make sure that you understand the implications of ignoring ensureSecurity before you use this code verbatim. See the MSDN page on RegisterChannel for more details about how this argument functions.

You’ll find that the remoting object is created in “Single Call” mode, which results in it being torn down and destroyed on the server after it is called. This may not be appropriate for remoting objects which have to contain state; in that case you’ll want to go with Singleton and deal with any lifecycle issues appropriately.

That said, if you end up using Singleton, it may be a better choice to simply redesign your application such that SingleCall can be used.