c# - ¿Cargar x64 o x86 DLL dependiendo de la plataforma?
.net (3)
En realidad tengo algo de experiencia en este tema, así que pensé que publicaría la respuesta de acuerdo con las formas que usé en Pencil.Gaming. En primer lugar, tiene que " DllImport
" dos funciones, una de la dll de 32 bits y otra de la dll de 64 bits (o lo que sea, dylib, cualquiera que sea su plataforma).
static class Foo32 {
[DllImport("32bitdll.dll")]
internal static extern void Foo();
}
static class Foo64 {
[DllImport("64bitdll.dll")]
internal static extern void Foo();
}
Luego necesita una clase intermedia que contenga delegados e importarlos desde la interoperabilidad de 32 o 64 bits de acuerdo con el tamaño de un IntPtr
(no uso Environment.Is64BitProcess
, ya que es una función .NET 4):
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);
}
}
}
Y luego generalmente uso una clase "real", que contiene la función que importó (aunque esto no es técnicamente necesario):
public static class FooApi {
public static void Foo() {
FooDelegates.Foo();
}
}
Esto es un verdadero problema si solo necesita una o dos funciones, pero la forma de importar delegados es realmente eficaz para bibliotecas / aplicaciones de mayor escala. Es posible que desee revisar Pencil.Gaming en github, ya que utiliza este método bastante ( here hay un ejemplo de cómo se usa mucho).
Otro beneficio de este método es que es 100% multiplataforma y no se basa en ninguna función de WinAPI.
Esta pregunta ya tiene una respuesta aquí:
Tengo una aplicación creada como ''Cualquier CPU'' y tengo dos Dlls de terceros de la misma biblioteca destinados a x86 y x64. Me gustaría incluir una de estas bibliotecas en tiempo de ejecución dependiendo de la plataforma que se ejecutaría en la máquina cliente. ¿Cuál sería la mejor manera de hacerlo?
Mi solución completa a mi problema fue mediante el uso del segundo enlace proporcionado por David Heffernan. Lo que hice fue 1. Se hizo referencia a una dll ficticia en el proyecto. 2. Especificó dos eventos pre-compilación
xcopy /y "$(SolutionDir)/Assemblies/Lib/x86/(Assembly name)*" "$(TargetDir)"
xcopy /y "$(SolutionDir)/Assemblies/Lib/x64/(Assemble name)*" "$(TargetDir)"
3. y en el inicio de la aplicación en el evento de resolución de ensamblaje, se modificó el ensamblaje correspondiente según la plataforma.
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;
};
Si estamos hablando de DLL no administradas, declare las invocaciones / p de esta manera:
[DllImport("DllName.dll")]
static extern foo();
Tenga en cuenta que no estamos especificando una ruta a la DLL, solo su nombre, que supongo que es el mismo para las versiones de 32 y 64 bits.
Luego, antes de llamar a cualquiera de sus p / invoca, cargue la biblioteca en su proceso. Hágalo p / invocando a la función de la API LoadLibrary
. En este punto, determinará si su proceso es de 32 o 64 bits y construirá la ruta completa a la DLL en consecuencia. Ese camino completo es lo que pasas a LoadLibrary
.
Ahora, cuando llama a su p / invoca para la biblioteca, serán resueltos por el módulo que acaba de cargar.
Para ensamblajes administrados, entonces puede usar Assembly.LoadFile
para especificar la ruta del ensamblaje. Esto puede ser un poco difícil de organizar, pero este excelente artículo muestra cómo: elegir automáticamente DLL de modo mixto de 32 o 64 bits . Hay muchos detalles relacionados con el modo mixto y las dependencias de DLL nativas que probablemente no sean relevantes para usted. La clave es el controlador de eventos AppDomain.CurrentDomain.AssemblyResolve
.