Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to support multiple architectures in a mixed managed/unmanaged environment?

Background

We have a .NET library that is referencing one of our unmanaged dlls, lets say:

  • DotNet.dll
  • Unmanaged.dll

Thus far, Unmanaged.dll is only 32-bit, so the DotNet.dll is marked with 32-bit CPU type.

64-bit support needs to be added. How to organize the dlls? IL code of DotNet.dll will be the same for both 32-bit and 64-bit versions.

Option 1

  • 32Bit Libraries Folder
    • DotNet.dll, CPU type of 32-bit
    • Unmanaged.dll, compiled as 32-bit
  • 64Bit Libraries Folder
    • DotNet.dll, CPU type of 64-bit
    • Unamanged.dll, compiled as 64-bit

In this case a developer using these libraries is forced to make 2 applications: a 32-bit one and a 64-bit one. But in this case the know exactly what is happening.

Option 2

This is the same as Option 1 except DotNet.dll has CPU Type of AnyCPU.

  • 32Bit Libraries Folder
    • DotNet.dll, CPU type of AnyCPU
    • Unmanaged.dll, compiled as 32-bit
  • 64Bit Libraries Folder
    • DotNet.dll, CPU type of AnyCPU
    • Unamanged.dll, compiled as 64-bit

I do not like this one because a developer using these libraries, when redistributing their application cannot do a good job of making their application not crash without setting the CPU type on their application:

  • If they use 32Bit Libraries Folder, on a 64-bit OS their process will crash
  • If they use 64Bit Libraries Folder, on a 32-bit OS their process will crash

This makes Option 1 superior to Option 2.

Option 3

  • Unmanaged_x32.dll, compiled as 32-bit
  • Unmanaged_x64.dll, compiled as 64-bit
  • DotNet.dll, CPU type of AnyCPU

DotNet.dll at runtime would determine what bitness it is being run under, then PInvoke the correct Unmanaged.dll.

Question(s)

  1. As a developer of these libraries, what option makes most sense?
  2. As a developer using the DotNet.dll library, what option makes most sense?
    1. For Option 3, if you are using the DotNet.dll, would you want to know that the library at runtime determines what Unmanaged.dll to use?
    2. What about redistribution of your application with these libraries?
  3. Is some option missing?
like image 771
earlNameless Avatar asked Jul 21 '10 15:07

earlNameless


3 Answers

I would choose option 3, compile the managed assembly for AnyCPU and name the unmanaged assemblies for their architecture. I see two separate concerns that factor into this decision:

Should the managed assembly be compiled for AnyCPU or a specific architecture?

I think .NET developers would not expect to have to reference a separate file for each architecture. I would use AnyCPU in order to have exactly one dll.

Should the dlls be named explicitly for their architecture?

If you use AnyCPU for the managed assembly, there is exactly one dll, so this is a moot point.

For the unmanaged assembly, there may be an expectation that files compiled for different architectures are named differently. From a technical standpoint, naming the files differently allows you to put the files for both architectures in the same directory; this means calling into a different file at runtime depending on the architecture, but this is not a huge burden. I would name the files differently.

like image 52
Kevin Kibler Avatar answered Oct 19 '22 22:10

Kevin Kibler


Option 3 seems like the easiest, while Option 1 seems like the safest. Just from the perspective of which library to call, it doesn't seem like it would be that difficult to manage them unless you're dealing with a huge number of calls. The main issue is that you're going to have to declare any given function twice, using different names for 32- and 64-bit versions, then just change the DllImport attribute to point to the proper target. Your stub function will have to decide at runtime which one to call.

Note that, logistics aside, there's no need to include both in your library folder. As long as you don't invoke a call to the "wrong" library, excluding it won't have any impact.

like image 32
Adam Robinson Avatar answered Oct 19 '22 22:10

Adam Robinson


What I do is this:

I ship DotNet.dll compiled for AnyCPU with portable P/Invoke directives (like the API declarations have to be).

I ship unmanaged_32.bin and unmanaged_64.bin and install only the correct one for architecture as unmanaged.dll at install time.

A trick that still works would be to install unmanaged.dll like so:

x86: C:\Program Files (x86)\Common Files\unmanaged.dll x64: C:\Program Files\Common Files\unmanaged.dll

If C:\Program Files\Common Files is in the system path, this will cause P/Invoke to grab the correct DLL automatically.

like image 24
Joshua Avatar answered Oct 19 '22 23:10

Joshua