The trick I use for V8.Net is this:
- Create a new C# "proxy interface" project with all the defines to switch between the different architectures. In my case the project was named
V8.Net-ProxyInterface
; example:
public unsafe static class V8NetProxy
{
#if x86
[DllImport("V8_Net_Proxy_x86")]
#elif x64
[DllImport("V8_Net_Proxy_x64")]
#else
[DllImport("V8_Net_Proxy")] // (dummy - NOT USED!)
#endif
public static extern NativeV8EngineProxy* CreateV8EngineProxy(bool enableDebugging, void* debugMessageDispatcher, int debugPort);
THIS is the project you will reference. DO NOT reference the next two:
Create two more projects to generate x64 and x86 versions of the library. This is VERY EASY: Just copy-n-paste to duplicate the .csproj
file in the same folder and renamed them. In my case the project file was renamed to V8.Net-ProxyInterface-x64
and V8.Net-ProxyInterface-x86
, then I added the projects to my solution. Open the project settings for each of them in Visual Studio and make sure the Assembly Name
has either x64 or x86 in the name. At this point you have 3 projects: the first "placeholder" project, and the 2 architecture-specific ones. For the 2 new projects:
a) Open the x64 interface project settings, go to the Build
tab, select All Platforms
for Platform
at the top, then enter x64
in Conditional compilation symbols
.
b) Open the x86 interface project settings, go to the Build
tab, select All Platforms
for Platform
at the top, then enter x86
in Conditional compilation symbols
.
Open Build->Configuration Manager...
and make sure that x64
is selected as the platform for x64 projects, and x86
is selected for the x86 projects, for BOTH Debug
AND Release
configurations.
Make sure the 2 new interface projects (for x64 and x86) output to the same location of your host project (see project setting Build->Output path
).
The final magic: In a static constructor for my engine I quickly attach to the assembly resolver:
static V8Engine()
{
AppDomain.CurrentDomain.AssemblyResolve += Resolver;
}
In the Resolver
method, I just load the file based on the current platform indicated by the current process (note: this code is a stripped-down version and not tested):
var currentExecPath = Assembly.GetExecutingAssembly().Location;
var platform = Environment.Is64BitProcess ? "x64" : "x86";
var filename = "V8.Net.Proxy.Interface." + platform + ".dll"
return Assembly.LoadFrom(Path.Combine(currentExecPath , fileName));
Finally, go to your host project in the solution explorer, expand References
, select the first dummy project you created in step 1, right-click it to open the properties, and set Copy Local
to false
. This allows you to develop with ONE name for each P/Invoke function, while using the resolver to figure out which one to actually load.
Note that the assembly loader only runs when needed. It is only triggered (in my case) automatically by the CLR system upon the first access to the engine class. How that translates to you depends on how your host project is designed.
DllImport
attribute altogether and load the DLL yourself, manually, using theLoadLibrary
,GetProcAddess
, andFreeLibrary
functions. That technique is discussed here. It's a fair bit of work, though, and rather simple to get wrong. Letting the P/Invoke mechanism do it for you is so much easier. As others have noted, probably not worth it if you can just fall back to the 32-bit DLL all the time as the lowest common denominator. – Karleen