I have an application built as 'Any CPU' and have two third party Dlls of the same library targeted to x86 and x64. I would like to include one of these libraries at runtime depending upon the platform it would run on the client machine. What would be the best way to go about it ?.
-
1Are those managed assemblies or native code? – Darin Dimitrov May 15 '13 at 15:35
-
Why you don't want to just build x86 and x64 versions of your application? – outcoldman May 15 '13 at 15:40
-
I would like to say they are mixed. De-compiling them gave me a whole lot of code written in C but I am still not sure. – Ammark May 15 '13 at 15:41
-
@outcoldman That would have solved all my problems but that would mean changing a lot of the build process and support for our users. – Ammark May 15 '13 at 15:50
-
1De-compiling won't be very informative. How do you call them? If you use p/invoke then the DLLs are unmanaged. – David Heffernan May 15 '13 at 15:55
-
@DavidHeffernan I don't use p/invoke so I assume they are managed ?. – Ammark May 15 '13 at 15:58
-
Well, how do you access them at the moment? We are all guessing here. – David Heffernan May 15 '13 at 16:01
-
@DavidHeffernan I include the library and directly access the class. – Ammark May 15 '13 at 16:05
-
Related, but for CF instead of desktop: http://stackoverflow.com/questions/3156875/choose-appropriate-platform-dependent-dll-at-runtime – Ben Voigt May 15 '13 at 17:27
-
If its managed why don't you just compile the class library as any cpu? – Yaur May 15 '13 at 17:41
-
It's a third party dll. – Ammark May 15 '13 at 17:59
3 Answers
If we are talking about unmanaged DLLs, declare the p/invokes like this:
[DllImport("DllName.dll")]
static extern foo();
Note that we are not specifying a path to the DLL, just its name, which I presume is the same for both 32 and 64 bit versions.
Then, before you call any of your p/invokes, load the library into your process. Do that by p/invoking to the LoadLibrary
API function. At this point you will determine whether your process is 32 or 64 bit and build the full path to the DLL accordingly. That full path is what you pass to LoadLibrary
.
Now, when you call your p/invokes for the library, they will be resolved by the module that you have just loaded.
For managed assemblies then you can use Assembly.LoadFile
to specify the path of the assembly. This can be a little tricky to orchestrate, but this excellent article shows you how: Automatically Choose 32 or 64 Bit Mixed Mode DLLs. There are a lot of details relating to mixed mode and the native DLL dependencies that are probably not relevant to you. The key is the AppDomain.CurrentDomain.AssemblyResolve
event handler.

- 601,492
- 42
- 1,072
- 1,490
-
-
Thanks David, Would I need to create a wrapper around the existing dlls, as the methods I need to access are not static ?. – Ammark May 15 '13 at 15:57
-
Do you use `DllImport` right now when you call your code when you compile just as x86 or just x64? – Scott Chamberlain May 15 '13 at 15:59
-
-
Then this is not the way to fix your problem (well, it may still be but the instructions will be slightly different), please edit and clarify your original question that you are linking to a **managaged** DLL, not a native DLL. **EDIT**:Never-mind, I just saw David's Edit. You want the bottom paragraph David put in. – Scott Chamberlain May 15 '13 at 16:22
I'm actually kind of experienced about this topic, so I thought I would post the answer according to the ways I used in Pencil.Gaming. Firstly, you have to "DllImport
" two functions, one from the 32-bit dll, and one from the 64-bit dll (or so, or dylib, whatever your platform uses).
static class Foo32 {
[DllImport("32bitdll.dll")]
internal static extern void Foo();
}
static class Foo64 {
[DllImport("64bitdll.dll")]
internal static extern void Foo();
}
Then you need an intermediate class containing delegates, and importing them from the 32 or 64-bit interop according to the size of an IntPtr
(I don't use Environment.Is64BitProcess
, since that's a .NET 4 function):
internal delegate void FooDelegate();
static class FooDelegates {
internal static FooDelegate Foo;
static FooDelegates() {
Type interop = (IntPtr.Size == 8) ? typeof(Foo64) : typeof(Foo32);
FieldInfo[] fields = typeof(FooDelegates).GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
foreach (FieldInfo fi in fields) {
MethodInfo mi = glfwInterop.GetMethod(fi.Name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
Delegate function = Delegate.CreateDelegate(fi.FieldType, mi);
fi.SetValue(null, function);
}
}
}
And then I generally use a "real" class, containing the function you imported (though this is not technically required):
public static class FooApi {
public static void Foo() {
FooDelegates.Foo();
}
}
This is a real pain if you only need one or two functions, but the way of importing delegates is really efficient for larger scale libraries/applications. You might want to check out Pencil.Gaming on github, as it uses this method quite extensively (here is an example of it being used a lot).
Another benefit of this method is that it's 100% cross-platform, and doesn't rely on any WinAPI functions.

- 5,702
- 2
- 26
- 33
-
1You don't need any delegates. You can do the whole thing with plain old `DllImport` for all imported functions. – David Heffernan May 15 '13 at 17:50
-
@DavidHeffernan But then you can't use "Any CPU". You will then need to redistribute separate versions of your application for 32 bit and 64 bit systems. – antonijn May 15 '13 at 18:59
-
Sure you can use AnyCPU. My answer explains how. You do need the DLLs to have the same name, but be in different folders mind you. But that's normal for DLLs with 32/64 bitness. – David Heffernan May 15 '13 at 19:03
-
@DavidHeffernan I'm sorry, I'm not even considering your solution. P/Invoking WinAPI functions is evil by definition IMO. – antonijn May 15 '13 at 19:04
-
So how do you get the delegates. You call `GetProcAddress` somewhere? – David Heffernan May 15 '13 at 19:22
-
@DavidHeffernan No. If you read the answer, you would find out. I import them using DllImport. – antonijn May 16 '13 at 11:15
-
"If you read the answer". No need to be so rude. I still don't quite follow your answer. Anyway, despite declaring p/invoke to be evil, you are happily using it. Yours in confusion! – David Heffernan May 16 '13 at 11:21
-
@DavidHeffernan I said P/Invoking **WinAPI** functions. I don't see any WinAPI functions here. – antonijn May 16 '13 at 15:04
-
You were the first person to mention WinAPI functions. Why did you bring those into the discussion? – David Heffernan May 16 '13 at 15:07
-
let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/30076/discussion-between-antonijn-and-david-heffernan) – antonijn May 16 '13 at 15:08
My complete solution to my problem was by using the second link provided by David Heffernan. What I did was 1. Referenced a dummy dll in the project. 2. Specified two pre-build events
xcopy /y "$(SolutionDir)\Assemblies\Lib\x86\(Assembly name)*" "$(TargetDir)"
xcopy /y "$(SolutionDir)\Assemblies\Lib\x64\(Assemble name)*" "$(TargetDir)"
3. and on startup of the app in the assembly resolve event changed the corresponding assembly depending upon the platform.
var currentDomain = AppDomain.CurrentDomain;
var location = Assembly.GetExecutingAssembly().Location;
var assemblyDir = Path.GetDirectoryName(location);
if (assemblyDir != null && (File.Exists(Path.Combine(assemblyDir, "(Assembly name).proxy.dll"))
|| !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x86.dll"))
|| !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x64.dll"))))
{
throw new InvalidOperationException("Found (Assembly name).proxy.dll which cannot exist. "
+ "Must instead have (Assembly name).x86.dll and (Assembly name).x64.dll. Check your build settings.");
}
currentDomain.AssemblyResolve += (sender, arg) =>
{
if (arg.Name.StartsWith("(Assembly name),", StringComparison.OrdinalIgnoreCase))
{
string fileName = Path.Combine(assemblyDir,
string.Format("(Assembly).{0}.dll", (IntPtr.Size == 4) ? "x86" : "x64"));
return Assembly.LoadFile(fileName);
}
return null;
};

- 381
- 1
- 6
- 17