Utiliser une dll 32bit ou 64bit dans C# DllImport

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

il y a une variable statique contenant le nom du fichier DLL: string DLL_FILE_NAME.

et 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'à présent.

comme vous pouvez l'imaginer, le logiciel est compilé avec CPU" est activée.

j'ai aussi le code suivant pour déterminer si le système doit utiliser le fichier 64bit ou le fichier 32bit.

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

maintenant vous devriez voir le problème.. DLL_FILE_NAME est défini dans le temps de compilation et non dans le temps d'exécution donc la dll correcte n'est pas chargée selon le 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 32bit et les autres 64bits)? Comment définir DLL_FILE_NAME avant il est utilisé dans la déclaration DllImport?

55
demandé sur Cody Gray 2012-06-01 18:56:22

7 réponses

j'ai trouvé le moyen le plus simple de faire ceci est d'importer les deux méthodes avec des noms différents, et d'appeler la bonne. La DLL ne sera pas chargée jusqu'à ce que l'appel est fait donc il est très bien:

[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 difficile à entretenir manuellement.

51
répondu Julien Lebosquain 2012-06-04 08:53:55

Voici une autre alternative qui exige que les deux DLLs aient le même nom et soient placés dans des dossiers différents. Par exemple:

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

le truc est de 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'utilisera.

cela peut être fait facilement dans le constructeur statique de la classe parent.

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(string dllToLoad);

    [DllImport("MyDll.dll")]
    public static extern int MyFunction(int var1, int var2);
}

EDIT 2017/02/01 : utiliser Assembly.CodeBase de sorte qu'il fonctionne même si Copie D'ombre est activé.

45
répondu Benoit Blanchon 2017-02-01 14:43:18

dans ce cas, je devrais faire comme ceci (faire 2 dossiers, x64 et x86 + mettre la dll correspondante, 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(string[] 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(string path);
}
14
répondu Kisded Szabolcs 2015-06-29 12:24:33

il y a une variable statique contenant le nom du fichier DLL

il ne s'agit pas d'une variable statique. C'est une constante, au moment de la compilation. Vous ne pouvez pas changer une constante de temps de compilation à l'exécution.

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

honnêtement, je recommande juste de cibler x86 et d'oublier la version 64 bits tous ensemble, et de laisser votre l'application s'exécute sur WOW64, à moins que votre application ait un besoin impérieux de s'exécuter sous x64.

S'il y a un besoin pour x64, vous pouvez:

  • changez les DLLs pour avoir le même nom, comme MyDll.dll , et au moment de l'installation / déploiement, mettez le bon en place. (Si L'OS est x64, déployer la version 64 bits de la DLL, sinon la version x86).

  • ont deux constructions séparées un pour x86 et un pour x64.

7
répondu vcsjones 2012-06-03 11:03:32

ce que vous décrivez est connu sous le nom de" side-by-side assembly " (deux versions du même ensemble, l'une 32 et l'autre 64 bits)... Je pense que vous trouverez ces utiles:

ici vous pouvez trouver une walkthrough pour exactement votre scénario (.NET DLL wrapping C++/CLI faisant référence à une DLL native).

recommandation:

Juste le construire comme x86 et être fait avec elle... ou avoir 2 constructions (une x86 et une x64)... comme les techniques ci-dessus sont plutôt complexes...

2
répondu Yahia 2017-05-23 12:18:13

une autre approche peut être

public static class Sample
{
    public Sample()
    {

        string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\";
        string 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);
}
1
répondu user2381106 2015-07-01 14:20:55

j'ai utilisé une des approches signifiées par vcsjones:

"Changement de la Dll d'avoir le même nom, comme Madll.dll, et au moment de l'installation / déploiement, mettre le bon en place."

cette approche nécessite le maintien de deux plates-formes de construction, mais voir ce lien pour plus de détails: https://stackoverflow.com/a/6446638/38368

0
répondu Danny Varod 2017-05-23 12:18:13