Utiliser une DLL 32 bits ou 64 bits en C # DllImport

Voici la situation, j’utilise un dll basé sur C dans mon application dot.net. Il y a 2 DLL, une est 32 bits appelée MyDll32.dll et l’autre est une version 64 bits appelée MyDll64.dll.

Il existe une variable statique contenant le nom du fichier DLL: ssortingng DLL_FILE_NAME.

et il est utilisé de la manière suivante:

[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")] private static extern int is_Func1(int var1, int var2); 

Simple jusqu’ici.

Comme vous pouvez l’imaginer, le logiciel est compilé avec “Any CPU” activé.

J’ai également le code suivant pour déterminer si le système doit utiliser le fichier 64 bits ou le fichier 32 bits.

 #if WIN64 public const ssortingng DLL_FILE_NAME = "MyDll64.dll"; #else public const ssortingng DLL_FILE_NAME = "MyDll32.dll"; #endif 

A présent, vous devriez voir le problème. DLL_FILE_NAME est défini au moment de la compilation et non au moment de l’exécution, de sorte que la bonne DLL n’est pas chargée en fonction du contexte d’exécution.

Quelle serait la bonne façon de traiter ce problème? Je ne veux pas deux fichiers d’exécution (un pour 32 bits et l’autre pour 64 bits)? Comment puis-je définir DLL_FILE_NAME avant son utilisation dans l’instruction DllImport?

J’ai trouvé le moyen le plus simple de procéder est d’importer les deux méthodes avec des noms différents et d’appeler le bon. La DLL ne sera pas chargée tant que l’appel n’aura pas été effectué, donc ça va:

 [DllImport("MyDll32.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)] private static extern int Func1_32(int var1, int var2); [DllImport("MyDll64.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)] private static extern int Func1_64(int var1, int var2); public static int Func1(int var1, int var2) { return IntPtr.Size == 8 /* 64bit */ ? Func1_64(var1, var2) : Func1_32(var1, var2); } 

Bien sûr, si vous avez beaucoup d’importations, cela peut devenir assez lourd à gérer manuellement.

Voici une autre alternative qui requirejs que les deux DLL portent le même nom et soient placées dans des dossiers différents. Par exemple:

  • win32/MyDll.dll
  • win64/MyDll.dll

L’astuce consiste à charger manuellement la DLL avec LoadLibrary avant que le CLR ne le fasse. Il verra alors qu’un MyDll.dll est déjà chargé et l’utilise.

Cela peut être fait facilement dans le constructeur statique de la classe parente.

 static class MyDll { static MyDll() { var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath; var myFolder = Path.GetDirectoryName(myPath); var is64 = IntPtr.Size == 8; var subfolder = is64 ? "\\win64\\" : "\\win32\\"; LoadLibrary(myFolder + subfolder + "MyDll.dll"); } [DllImport("kernel32.dll")] private static extern IntPtr LoadLibrary(ssortingng dllToLoad); [DllImport("MyDll.dll")] public static extern int MyFunction(int var1, int var2); } 

EDIT 2017/02/01 : Utilisez Assembly.CodeBase pour qu’il fonctionne même si la copie fantôme est activée.

Dans ce cas, je devrais faire comme ça (faire 2 dossiers, x64 et x86 + mettre le dll correspondant, AVEC LE MÊME NOM, dans les deux dossiers):

 using System; using System.Runtime.InteropServices; using System.Reflection; using System.IO; class Program { static void Main(ssortingng[] args) { var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86"); bool ok = SetDllDirectory(path); if (!ok) throw new System.ComponentModel.Win32Exception(); } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool SetDllDirectory(ssortingng path); } 

Il existe une variable statique contenant le nom du fichier DLL

Ce n’est pas une variable statique. C’est une constante, à la compilation. Vous ne pouvez pas modifier une constante de compilation lors de l’exécution.

Quelle serait la bonne façon de traiter ce problème?

Honnêtement, je vous recommande de cibler x86 et d’oublier la version 64 bits, et de laisser votre application s’exécuter sur WOW64, à moins que votre application ait besoin d’être exécutée en x64.

Si x64 est nécessaire, vous pouvez:

  • Modifiez les DLL pour MyDll.dll portent le même nom, telles que MyDll.dll , et au moment de l’installation / du déploiement, mettez la bonne en place. (Si le système d’exploitation est x64, déployez la version 64 bits de la DLL, sinon la version x86).

  • Avoir deux versions distinctes, une pour x86 et une pour x64.

Ce que vous décrivez est appelé “assemblage côte à côte” (deux versions du même assemblage, l’une 32 et l’autre 64 bits) … Je pense que vous trouverez ces informations utiles:

Vous trouverez ici une procédure détaillée pour votre scénario (DLL .NET enveloppant les DLL C ++ / CLI faisant référence à une DLL native).

RECOMMANDATION:

Il suffit de le construire en x86 et de le faire avec … ou d’avoir 2 versions (une x86 et une x64) … car les techniques ci-dessus sont plutôt compliquées …

une approche alternative peut être

 public static class Sample { public Sample() { ssortingng StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\"; ssortingng ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll"; if (!File.Exists(ResolvedDomainTimeFileName)) { if (Environment.Is64BitProcess) { if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll")) File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName); } else { if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll")) File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName); } } } [DllImport("ABCLib__Resolved.dll")] private static extern bool SomeFunctionName(ref int FT); } 

J’ai utilisé l’une des approches citées par vcsjones:

“Modifiez les DLL pour qu’elles portent le même nom, telles que MyDll.dll, et au moment de l’installation / du déploiement, mettez la bonne en place.”

Cette approche nécessite de maintenir deux plates-formes de construction, mais consultez ce lien pour plus de détails: https://stackoverflow.com/a/6446638/38368