9 Stimmen

Dllimport programmatisch in C# einstellen

Ich verwende DllImport in meiner Lösung.
Mein Problem ist, dass ich zwei Versionen der gleichen DLL eine für 32 Bit und eine andere für 64 Bit gebaut haben.

Beide stellen die gleichen Funktionen mit identischen Namen und identischen Signaturen zur Verfügung. Mein Problem ist, dass ich zwei statische Methoden verwenden muss, die diese exponieren und dann zur Laufzeit verwenden IntPtr Größe, um den richtigen Aufruf zu ermitteln.

private static class Ccf_32
{
    [DllImport(myDllName32)]
    public static extern int func1();
}

private static class Ccf_64
{
    [DllImport(myDllName64)]
    public static extern int func1();
}

Ich muss dies tun, weil myDllName32 y myDllName64 muss konstant sein, und ich habe keine Möglichkeit gefunden, sie zur Laufzeit zu setzen.

Hat jemand eine elegante Lösung für diese so könnte ich loswerden der Code-Duplikation und die konstante IntPtr Größenkontrolle.

Wenn ich den Dateinamen festlegen könnte, bräuchte ich nur einmal zu prüfen, und ich könnte eine Menge wiederholten Code loswerden.

0 Stimmen

Es macht keinen Sinn, sie zur Laufzeit auszuwählen, wenn der Unterschied in der gesamten Kompilierung besteht.

20voto

Josh Sklare Punkte 3864

Ich ziehe es vor, dies mit der Option LoadLibrary Aufruf von kernel32.dll, um das Laden einer bestimmten DLL von einem bestimmten Pfad zu erzwingen.

Wenn Sie Ihre 32-Bit- und 64-Bit-DLLs gleich benennen, sie aber in unterschiedlichen Pfaden ablegen, können Sie den folgenden Code verwenden, um die richtige DLL je nach der von Ihnen verwendeten Windows-Version zu laden. Alles, was Sie tun müssen, ist der Aufruf ExampleDllLoader.LoadDll() BEVOR jeglicher Code, der auf die ccf Klasse referenziert wird:

private static class ccf
{
    [DllImport("myDllName")]
    public static extern int func1();
}

public static class ExampleDllLoader
{
    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    private extern static IntPtr LoadLibrary(string librayName);

    public static void LoadDll()
    {
        String path;

        //IntPtr.Size will be 4 in 32-bit processes, 8 in 64-bit processes 
        if (IntPtr.Size == 4)
            path = "c:/example32bitpath/myDllName.dll";
        else
            path = "c:/example64bitpath/myDllName.dll";

        LoadLibrary(path);
    }
}

0 Stimmen

Es sieht so aus, als ob diese Lösung voraussetzt, dass der Benutzer über Administratorrechte verfügt. Kann das jemand bestätigen?

1 Stimmen

Dazu sind keine Administratorrechte erforderlich, solange der Benutzer die DLL, die geladen wird, lesen darf. Ich habe es in Situationen verwendet, in denen ein normaler Benutzer die Anwendung ausführt.

13voto

Fredrik Mörk Punkte 151006

Sie können dies wahrscheinlich mit dem #if Stichwort. Wenn Sie ein bedingtes Compilersymbol namens win32 Der folgende Code verwendet den win32-Block, wenn Sie ihn entfernen, wird der andere Block verwendet:

#if win32
    private static class ccf_32
    {
        [DllImport(myDllName32)]
        public static extern int func1();
    }
#else    
    private static class ccf_64
    {
        [DllImport(myDllName64)]
        public static extern int func1();
    }
#endif

Dies bedeutet wahrscheinlich, dass Sie die Klassenumhüllung, die Sie jetzt haben, entfernen können:

    private static class ccf
    {
#if win32
        [DllImport(myDllName32)]
        public static extern int func1();
#else    
        [DllImport(myDllName64)]
        public static extern int func1();
#endif
    }

Der Einfachheit halber könnten Sie Build-Konfigurationen zur Steuerung des Kompilierungssymbols erstellen.

3 Stimmen

Ja, aber ich würde gerne die Option "Jede CPU" beibehalten, anstatt 32- und 64-Bit-Versionen zu haben.

0 Stimmen

Obwohl es funktioniert, führt dieser Ansatz immer noch zu der Code-Duplizierung, die der ursprüngliche Poster vermeiden möchte, und schränkt Sie ein, separate Versionen für 32 und 64 Bit zu kompilieren. Sie können die Code-Duplizierung zu entfernen und weiterhin sowohl 32 und 64-Bit-Versionen mit einem kompilierten DLL/EXE Ziel, indem Sie deanis Ansatz zu SetDllDirectory verwenden, oder meine LoadLibrary verwenden.

11voto

deanis Punkte 475

Ich weiß, dies ist eine wirklich alte Frage (ich bin neu - ist es schlecht, eine alte Frage zu beantworten?), aber ich hatte gerade dieses gleiche Problem zu lösen. Ich musste dynamisch auf eine 32-Bit- oder 64-Bit-DLL verweisen, je nach Betriebssystem, während meine EXE für jede CPU kompiliert wird.

Sie können DLLImport verwenden, und Sie müssen LoadLibrary() nicht verwenden.

Dazu habe ich Folgendes verwendet SetDLLDirectory . Im Gegensatz zum Namen, SetDLLDirectory wird dem DLL-Suchpfad hinzugefügt und ersetzt nicht den gesamten Pfad. Dadurch konnte ich eine DLL mit demselben Namen ("TestDLL.dll" für diese Diskussion) in Win32- und Win64-Unterverzeichnissen haben, die entsprechend aufgerufen wurde.

public partial class frmTest : Form
{
    static bool Win32 = Marshal.SizeOf(typeof(IntPtr)) == 4;
    private string DLLPath = Win32 ? @"\Win32" : @"\Win64";

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool SetDllDirectory(string lpPathName);
    [DllImport("TestDLL.dll", SetLastError = true)]
    static extern IntPtr CreateTestWindow();

    private void btnTest_Click(object sender, EventArgs e)
    {
        string dllDir = String.Concat(Directory.GetCurrentDirectory(), DLLPath);
        SetDllDirectory(dllDir);

        IntPtr newWindow = CreateTestWindow();
    }
}

3 Stimmen

Dies ist ein guter Ansatz, vor allem, wenn Sie mehrere DLLs haben, die Sie in 32- und 64-Bit-Versionen benötigen. Ich glaube, mir gefällt der SetDllDirectory-Ansatz sogar besser als die LoadDll-Methode, die ich in meiner Antwort erklärt habe. Es ist auch eine ganze Menge weniger Code als die Wrapper-Ansätze, und lassen Sie kompilieren Any CPU.

2voto

ChaosPandion Punkte 75527

Warum sollte man sie nicht in eine Methode verpacken?

private static class ccf_32_64
{
    private static class ccf_32
    {
        [DllImport(myDllName32)]
        private static extern int func1();
    }

    private static class ccf_64
    {
        [DllImport(myDllName64)]
        private static extern int func1();
    }

    public static int func1()
    {
        if (32bit)
        {
            return ccf_32.func1();
        }
        else
        {
            return ccf_64.func1();
        }
    }
}

0 Stimmen

Das ist im Wesentlichen das, was ich jetzt habe :-)

0 Stimmen

Nun, wenn Sie es einmal eingepackt haben, müssen Sie sich keine Sorgen mehr darüber machen.

2voto

Zack Elan Punkte 1646

Eine alternative Möglichkeit besteht darin, die 32- und 64-Bit-Versionen der nicht verwalteten DLL mit demselben Namen zu versehen, sie aber in getrennten Ordnern in der Build-Ausgabe zu speichern (z. B. x86\ und x64\).

Dann wird Ihr Installationsprogramm oder wie auch immer Sie es verteilen, aktualisiert, so dass es weiß, dass es die richtige DLL für die Plattform, auf der es installiert wird, installieren muss.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X