Ich versuche, eine 32-Bit-DLL (und Anwendung) auf 64-Bit zu portieren, und ich habe es geschafft, es ohne Fehler zu bauen. Beim Versuch, es mit meiner 64-Bit-Anwendung zu laden, bemerkte ich, dass die exportierten Funktionsnamen sich unterscheiden. So exportiere ich die Funktionen:
%Vor%In Dependency Walker haben die exportierten Funktionen das folgende Format:
32-Bit: _Connect@8
64-Bit: Connect
In der Anwendung, die die DLL verwendet, lade ich explizit die DLL (LoadLibrary ist erfolgreich), aber GetProcAddress schlägt für 64-Bit fehl, weil es keine Funktion mit dem angegebenen Namen finden kann.
In unserer Anwendung behalte ich die Funktionsnamen wie folgt:
%Vor%Ich habe mich also gefragt, ob es möglich ist, die gleichen Funktionsnamen für 32-Bit- und 64-Bit-DLLs zu exportieren, oder ist das eine schlechte Idee? Oder muss ich in meinen Anwendungen Folgendes tun:
%Vor%Ich schätze jede Hilfe.
Eine Option besteht darin, Funktionsnamen ohne Dekoration ( unabhängig aus der Aufrufkonvention, die Sie in x86 verwendet haben, __stdcall
, __cdecl
oder andere) und mit zu exportieren Derselbe undecorated Name in x86- und x64-Builds besteht darin, Ihre DLL-Funktionen mithilfe von zu exportieren DEF-Dateien .
z. Sie können eine .DEF-Datei wie folgt zu Ihrem Projekt hinzufügen:
%Vor%Repro folgt
Erstellen Sie eine leere Lösung in Visual Studio (ich verwendete VS2013), und erstellen Sie darin ein leeres Win32-Konsolenprojekt (den Testclient ) und ein leeres Win32-DLL-Projekt (die test DLL) ).
Fügen Sie diese NativeDll.def
.DEF-Datei im DLL-Projekt hinzu:
Fügen Sie diesen NativeDll.cpp
C ++ - Quellcode im DLL-Projekt hinzu:
Fügen Sie diesen NativeClient.cpp
C ++ - Quellcode im Client-Testprojekt hinzu:
Erstellen Sie die gesamte Lösung (sowohl die EXE-Datei als auch die DLL-Datei), und führen Sie den systemeigenen EXE-Client aus Das bekomme ich auf meinem Computer:
Es funktioniert ohne Änderungen und mit dem Funktionsnamen undecorated (nur SayHello
) für beide x86- und x64-Builds .
Wie Sie sehen können, sind in 64-Bit Windows Namen nicht dekoriert.
In den 32-Bit-Symbolen __cdecl
und __stdcall
wird dem Symbolnamen ein Unterstrich vorangestellt. Der nachgestellte '@ 8' im exportierten Namen für die 32-Bit-Version Ihrer Beispielfunktion ist die Anzahl der Bytes in der Parameterliste. Es ist da, weil Sie __stdcall
angegeben haben. Wenn Sie die Aufrufkonvention __cdecl
(die Standardeinstellung für C / C ++ - Code) verwenden, erhalten Sie das nicht. Wenn Sie __cdecl
verwenden, ist es viel einfacher, GetProcAddress()
mit etwas wie:
dann ruf einfach mit
an %Vor%oder etwas ähnliches (die Fehlerüberprüfung wurde im Beispiel weggelassen). Denken Sie daran, Ihre exportierten Funktionen wie folgt zu deklarieren:
%Vor% Zusätzlich zur einfacheren Pflege, wenn sich während der Entwicklung die Signatur einer exportierten Funktion ändert, müssen Sie nicht mit Ihren #define
-Wrappern herumspielen.
Nachteil: Wenn sich während der Entwicklung die Anzahl der Bytes in der Parameterliste einer bestimmten Funktion ändert, wird sie nicht von der Anwendung, die die Funktion importiert, abgefangen, weil die Änderung der Signatur den Namen nicht ändert. Persönlich glaube ich nicht, dass dies ein Problem ist, da der 64-Bit-Build unter den gleichen Umständen sowieso explodieren würde, da die Namen nicht dekoriert sind. Sie müssen nur sicherstellen, dass Ihre Anwendung die richtige Version der DLL verwendet.
Wenn der Benutzer der DLL C ++ verwendet, können Sie die Dinge mithilfe von C ++ - Funktionen besser umbrechen (die gesamte explizit geladene Bibliothek in eine Wrapperklasse einbetten, z. B..):
%Vor%Es gibt tatsächlich viel mehr, was Sie mit einer Wrapper-Klasse wie dieser tun können, es ist nur ein Beispiel.
On edit: da OP erwähnt mit PInvoke in den Kommentaren - wenn jemand entscheidet, dies zu tun, nicht vergessen hinzufügen CallingConvention = CallingConvention.Cdecl
in der [DllImport]
-Deklaration, wenn PInvoke verwenden. __cdecl
ist möglicherweise der Standard für nicht verwaltetes C / C ++, ist aber nicht der Standardwert für verwalteten Code.
__stdcall
wird auf x64 nicht unterstützt (und wird ignoriert). Zitieren MSDN :
Bei ARM- und x64-Prozessoren wird __ stdcall vom Compiler akzeptiert und ignoriert. Bei ARM- und x64-Architekturen werden die Argumente nach Möglichkeit in Registern übergeben, und nachfolgende Argumente werden auf dem Stapel übergeben.
Die Aufrufkonvention für x64 ist ziemlich viel __fastcall
.
Da sich die Aufrufkonventionen und die Regeln für die Namensdekoration auf x86 und x64 unterscheiden, müssen Sie dies irgendwie abstrahieren. Deine Idee mit #if _WIN64
geht also in die richtige Richtung.
Sie können x86-Aufrufkonventionen und Ihre Bedürfnisse untersuchen und vielleicht ein Makro entwickeln, das den Prozess der Namensauswahl automatisieren könnte.
Tags und Links c++ dll 64bit name-mangling