calling 64-bit code from a 32-bit .NET process using remoting
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.