Automatically Choose 32 or 64 Bit Mixed Mode DLL’s
Here’s a problem that was bugging me at my last job that I finally got around to solving last week on the bus: letting your top-level project run as AnyCPU and automatically choose a 32-bit or 64-bit low level native/managed DLL based on environment bit width.
Say you’re using Shawn Hladky’s great P4.Net project (source) so your C# can speak Perforce – two great tastes that taste great together. P4.Net talks to the server using a native Perforce API. Now, unlike a C# EXE, which is usually AnyCPU (i.e. “let the jitter decide”), the native code that talks to the Perforce server must be compiled as either x86 or x64. This causes two big problems.
The first problem is that your app will crash with a confusing error if run on the wrong version of the .Net Framework! Say you’re using the x86 P4.Net and you run your app on Win7-x64. The jitter will compile the app in 64 bit, but on the first reference to P4.Net, it tries and fails to load the x86 DLL, and puts up an unhelpful error about a bad image format.
The easy workaround to this, of course, is to mark your EXE as x86 instead of AnyCPU. Solved. Unfortunately, you have to remember to do this with every single app that references P4.Net. Or references a DLL that indirectly references P4.Net. It’s like a virus in that way, but we can handle it.
Well, no. That leads to the second problem. What happens if you reference P4.Net (directly or indirectly) into an app that really does need 64 bit? Like, say, some memory-hoovering game build related tool that runs on the server farm? Well now you need a 64-bit version, not only of P4.Net, and not only of your EXE, but of every single DLL that is referenced on the path down to your new P4.Net_x64.DLL. Now we have a real problem. The pain in the ass to maintain kind of problem. Do we really want to have our tool chain output 32 and 64 bit versions of everything, just in case?
Had Microsoft supported fat binaries like NeXTSTEP did back in the early 90’s, we’d just have P4.Net with 32 and 64 bit in the same DLL, and go on with our lives. But no, we have to jump through hoops. This article is the story of how to jump through those hoops to make your bits go.
I’m actually a bit shocked that Microsoft hasn’t extended PE and their OS loaders to support fat binaries. There would be zero perf cost, and it’s been a long time since we had to worry about the size of binaries on disk (content overwhelms executable code size in nearly every app today).
About P4.Net
If I’m going to continue to use P4.Net for my example, I need to give a little more background. P4.Net is built from three components:
- p4api.lib: A native C++ API (headers and libs) provided by Perforce to talk to their server directly through sockets, without running p4.exe or using the COM object.
- p4dn.dll: A bridge assembly that statically links in p4api, and uses Managed C++ to export p4api as a low level set of .Net types.
- p4api.dll: A managed C# API that wraps up the low level p4dn and adds functionality to make it easier to work with. This is what everybody does an “add reference” on to talk to P4 from C#.
Note that p4api.dll is not strictly necessary as a separate assembly. The low level types exported by p4dn.dll could instead be kept internal, and all of that C# code from p4api.dll be written in Managed C++ and moved into p4dn.dll, entirely eliminating the need for p4api.dll.
Personally I was a fan of Managed C++, up until C# 3.0 where we started getting all kinds of nice language syntax to write better code more compactly. Today, I suppose I’d keep the extra DLL just for easier maintenance.
The Solution
In a nutshell, the solution is to trick the loader! Reference a p4dn.dll that does not exist, and use the AssemblyResolve event to intercept the load and reroute it to the correct bit size assembly.
It’s simple in concept but has a lot of details that took me a whole bus ride to figure out all the way (happily, there was a lot of traffic). Here is what I ended up doing to make it work how I wanted:
- Rename the x86 output of p4dn.dll to p4dn.proxy.dll.
- Update the x86 linker input settings to add __DllMainCRTStartup@12 to Force Symbol References.
- Build a new x64 configuration for p4dn, using the x86 configuration (well, ‘Win32’) as a template. Have it output to p4dn.x64.dll.
- Update the x64 linker input settings to add _DllMainCRTStartup to Force Symbol References.
- Add a post-build event to p4dn’s x86 configuration that deletes p4dn.x86.* and copies the p4dn.proxy.* to p4dn.x86.*.
- Update p4api to reference p4dn.proxy.dll. Not the csproj, but the actual DLL.
- Update the SLN settings to make p4api dependent on p4dn.
- Add a post-build step to p4api to delete p4dn.proxy.dll.
- Set all p4api and p4dn configurations to output to the same bin folder.
- Add a static constructor to P4API.P4Connection that registers an event handler on AppDomain.CurrentDomain.AssemblyResolve to pick the right DLL when the proxy is requested. I’ve pasted my code at the bottom of this post.
Once this is done, you’ll be able to have p4api as well as any assemblies that reference it set to AnyCPU. It will, upon first usage of the P4Connection class, fail to resolve the proxy and reroute to the correct bit width DLL.
A few notes on the above:
- The _DllMainCRTStartup is required because, without it, I got a crash from uninitialized memory systems in the CRT DLL’s that p4dn was linked to. This happened regardless of static vs. dynamic linking. I didn’t bother to find out the real reason for it. The different symbol names for 32 bit vs. 64 bit are because the convention changed when Microsoft went to 64 bit.
- The name of the DLL being referenced must match the original name of the DLL being built. That is, if you were to have p4dn outputting to p4dn.x86.dll then renaming it to proxy, and then referencing that, then it will actually look for the referenced DLL’s “true” name of p4dn.x86.dll and never call your hook.
- In projects that reference p4api it’s best to set the references to non-private (clear the “Copy Local” flag) and have a post-build step that just copies whatever is in the p4api bin folder. That makes sure you get the exact files that you need. None of this will work if you accidentally end up with the proxy file existing.
Here’s the code for my hook function. Note that it attempts to catch problems with the post-build scripts.
static P4Connection()
{
string assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if ( File.Exists(Path.Combine(assemblyDir, "p4dn.proxy.dll"))
|| !File.Exists(Path.Combine(assemblyDir, "p4dn.x86.dll"))
|| !File.Exists(Path.Combine(assemblyDir, "p4dn.x64.dll")))
{
throw new InvalidOperationException("Found p4dn.proxy.dll which cannot exist. "
+ "Must instead have p4dn.x86.dll and p4dn.x64.dll. Check your build settings.");
}
AppDomain.CurrentDomain.AssemblyResolve += (_, e) =>
{
if (e.Name.StartsWith("p4dn.proxy,", StringComparison.OrdinalIgnoreCase))
{
string fileName = Path.Combine(assemblyDir,
string.Format("p4dn.{0}.dll", (IntPtr.Size == 4) ? "x86" : "x64"));
return Assembly.LoadFile(fileName);
}
return null;
};
}
Microsoft, if you’re listening: FAT BINARIES.
Quickie: Adding Utility Functions To Interfaces
In working in C#, one thing I miss from C++ is being able to implement interfaces via a rich base class. C#’s lack of multiple inheritance (a good decision on balance) just about prevents this.
I have two things in mind when I say “rich base class”. First is for mixing in object functionality. I’ve spoken about this before. There really is no way to do this nicely in C#. However, today’s post is about the other thing C++ gives you with multiple inheritance: utility methods.
Consider this interface and implementing class, paying attention to the overloads:
interface ILogger
{
void Log(string message);
void LogLine(string message);
void Log(string format, params object[] args);
void LogLine(string format, params object[] args);
}
class DebugLogger : ILogger
{
void ILogger.Log(string message)
{ Debug.Write(message); }
void ILogger.LogLine(string message)
{ Debug.WriteLine(message); }
void ILogger.Log(string format, params object[] args)
{ Debug.Write(string.Format(format, args)); }
void ILogger.LogLine(string format, params object[] args)
{ Debug.WriteLine(string.Format(format, args)); }
}
The more versions of the log function you want, the more work you have to do in any class implementing the interfaces. You could create a LoggerBase that does all of this, but that is severely limiting.
In C++ it’s easy of course. Make a mixin class.
class Logger
{
public:
void Log(const char* message)
{ OnLog(message); }
void LogLine(const char* message)
{ OnLog(message); OnLog("\n"); }
void LogF(const char* format, ...)
{
char buffer[2000];
va_list args;
va_start(args, format);
vsprintf_s(buffer, format, args);
va_end(args);
Log(buffer);
}
void LogLineF(const char* format, ...)
{
char buffer[2000];
va_list args;
va_start(args, format);
vsprintf_s(buffer, format, args);
va_end(args);
LogLine(buffer);
}
protected:
virtual void OnLog(const char* message) = 0;
};
class DebugLogger : public Logger
{
virtual void OnLog(const char* message)
{
OutputDebugString(message);
}
};
Exactly one virtual method is required in the derived class. To add new overloads, you just add them to the base, and have them call the virtual. All end up with dynamic behavior. With interfaces in C#, you’re forced to implement each overload. This ends up with a lot of duplication everywhere you implement this same interface. Worse, if you want to add more utility functions to the interface, you break every implementing class, which must now implement that function as well.
The other day, it hit me that there’s an easy and perhaps obvious solution to this in C#: extension methods.
interface ILogger
{
void Log(string message);
}
static partial class Extensions
{
public static void LogLine(this ILogger logger, string message)
{
logger.Log(message);
logger.Log("\n");
}
public static void LogFormat(this ILogger logger, string format, params object[] args)
{
logger.Log(string.Format(format, args));
}
public static void LogLineFormat(this ILogger logger, string format, params object[] args)
{
logger.LogFormat(format, args);
logger.Log("\n");
}
}
class DebugLogger : ILogger
{
void ILogger.Log(string message)
{
Debug.Write(message);
}
}
This will do what I want. I can implement a minimal interface, and easily add new utility functions. It’s not quite as good as C++, because I can’t store any data in my extension methods, and I must work solely through the published interface, but it’s good enough for 80%.
In one very small way it’s actually better than C++. In C++, that base class isn’t always something that can be changed. Perhaps it was provided by a standard library or a third party. Yet in C#, anybody can create an extension class to add functionality to any other class. So you can add all the overloads you like.
Assertive Finalizers
In my previous post, I talked about why I have stopped using finalizers for unmanaged resource collection. I want this to be done through the disposable pattern instead, forcing the programmer to manage resources manually.
Ironically, finalizers are a great way to verify this.
Sander van Rossen quickly figured out where I was going with this and proposed in a comment that we can just assert in a finalizer. We just need a couple more things:
- We need to track the source of the object to figure out where the leak is.
- We need to ensure that finalizers run on shutdown, or our assert will never get hit.
This is familiar territory to C++ programmers. Most of us use memory management libraries that provide leak detection and reporting. Let’s do something similar in C#.
DisposableBase
If you look around online, you’ll find some full-featured IDisposable base classes intended to deal with the full finalization model. We can eliminate most of that as either too complicated or unnecessary. We just need a few things:
- A stack trace grabbed at construction time
This will be used for the error report. Because this is expensive to gather, we need to control it with an #if that is off by default, only turned on if needed to help an investigation. Most of the time a leaked disposable will be easy to find by inspection, but in the 5% case we will need a lot more context. - A finalizer that reports the problem
It could throw an exception, fire an assert, or route to an error reporter. Depends on the application. In my example I just have it output to the debug window for a demo. - Disposal helper methods
These wrap up disposal a bit, so inheritors only need to implement OnDispose. The most important feature, though, is that Dispose will call GC.SuppressFinalize when our object is disposed. This eliminates the performance cost of having a finalizer in the normal case when clients are disposing this class properly. This is why the finalizer has no “if” in it – if it ever gets called, then we have a bug.
This is what I am currently using as my base class for handling unmanaged resources:
// comment out unless diagnosing a leak
#define DEBUG_DISPOSE
public abstract class DisposableBase : IDisposable
{
// store stack at point of construction for possible later use
# if DEBUG_DISPOSE
StackTrace _trace = new StackTrace(true);
# endif
// finalizer will not be called if object was properly disposed
~DisposableBase()
{
string message = "!! Forgot to dispose a " + GetType().FullName;
# if DEBUG_DISPOSE
message += "\n\nStack at construction:\n\n" + _trace + "!!";
# endif
Debug.WriteLine(message);
}
public bool IsDisposed { get; private set; }
public void Dispose()
{
ThrowIfDisposed();
IsDisposed = true;
try { OnDispose(); }
finally { GC.SuppressFinalize(this); }
}
protected abstract void OnDispose();
protected void ThrowIfDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException(GetType().FullName);
}
}
Demonstration
Here is a simple test app that shows what happens if we forget to dispose an instance.
public class DatabaseConnection : DisposableBase
{
protected override void OnDispose()
{ Debug.WriteLine("Disposing"); }
}
public class Program
{
static void Main(string[] args)
{
using (var remembered0 = new DatabaseConnection())
using (var remembered1 = new DatabaseConnection())
{
}
var forgotten = new DatabaseConnection();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
The first two instances dispose fine, but the third is leaked and so we get a log to the output window:
Disposing Disposing !! Forgot to dispose a DatabaseConnection Stack at construction: at DisposableBase..ctor() in C:\Users\Scott\Cloud\Proj\tests\BlogSamples\ConsoleApplication1\Finalizers.cs:line 18 at DatabaseConnection..ctor() at Program.Main(String[] args) in C:\Users\Scott\Cloud\Proj\tests\BlogSamples\ConsoleApplication1\Finalizers.cs:line 89 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() !!
We can easily zero in on the exact spot where the leaked resource was allocated and wrap it in a ‘using’ to resolve.
Note the use of GC.Collect and GC.WaitForPendingFinalizers right before the test application exits. This is necessary in order to force all finalizing objects to be collected and reported before process shutdown, otherwise they are simply dropped by the system when the process’s memory is released.
These two can even be called during normal app run as well for leak testing without needing to wait for shutdown. This would be useful in a live service with a periodic leak for which we want an in-session nonfatal log of leaks.
What About External Classes?
This takes care of our own classes, where we have full control. But what about system or third party classes that are using finalizers? With those it’s back to square one.
Well, I suppose would write a helper class..
public class SafeDisposer<T> : DisposableBase where T : IDisposable
{
public SafeDisposer(T disposable) { Obj = disposable; }
public T Obj { get; private set; }
protected override void OnDispose()
{
Obj.Dispose();
Obj = default(T);
}
}
public static class SafeDisposer
{
public static SafeDisposer<T> Wrap<T>(T disposable) where T : IDisposable
{ return new SafeDisposer<T>(disposable); }
}
And equivalent updates in the demo code:
public class Program
{
static void Main(string[] args)
{
using (var remembered2 = SafeDisposer.Wrap(new StringReader("foo")))
using (var remembered3 = SafeDisposer.Wrap(new StringReader("poo")))
{
remembered2.Obj.Peek();
}
var forgotten2 = SafeDisposer.Wrap(new StringReader("boo"));
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
And output:
!! Forgot to dispose a SafeDisposer`1[[System.IO.StringReader, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] Stack at construction: at DisposableBase..ctor() in C:\Users\Scott\Cloud\Proj\tests\BlogSamples\ConsoleApplication1\Finalizers.cs:line 18 at SafeDisposer`1..ctor(T disposable) in C:\Users\Scott\Cloud\Proj\tests\BlogSamples\ConsoleApplication1\Finalizers.cs:line 53 at SafeDisposer.Wrap[T](T disposable) in C:\Users\Scott\Cloud\Proj\tests\BlogSamples\ConsoleApplication1\Finalizers.cs:line 67 at Program.Main(String[] args) in C:\Users\Scott\Cloud\Proj\tests\BlogSamples\ConsoleApplication1\Finalizers.cs:line 98 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() !!
So to use, we just wrap a disposable at the point of creation in SafeDisposer.Wrap() and access it through “.Obj”. Not bad, but not great either. I’m ok with the wrapper function, but the required access via the Obj member is a pain, and also means that converting an unsafe disposable into a safe one requires updating a lot of code.
Another option is (maybe) to use a dynamic proxy system to inject the functionality we need, letting client code remain unchanged except at the point of creation. Or perhaps a run-time system that patches system assemblies to do the injection.
I’ll leave this as an exercise for the reader because I think we’re probably getting seriously diminishing returns at this point. Most disposable objects in a large system will be classes that we have full control over. The few objects not under our control will likely be low level primitives that will be wrapped up by our own foundation classes anyway.

