Wie Sie sehen können, sind in 64-Bit-Windows-Namen nicht verziert.
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, macht es viel einfacher zu wickeln GetProcAddress()
mit so etwas wie:
#if _WIN64
#define DecorateSymbolName(s) s
#else
#define DecorateSymbolName(s) "_" ## s
#endif
dann rufen Sie einfach mit
pfnConnect = GetProcAddress(hDLL, DecorateSymbolName("Connect"));
pfnOtherFunc = GetProcAddress(hDLL, DecorateSymbolName("OtherFunc"));
oder etwas ähnliches (Fehler in Beispiel weggelassen Prüfung). Dazu merken Sie sich Ihre exportierten Funktionen zu erklären:
__declspec(dllexport) long __cdecl Connect(char * name, long size);
__declspec(dllexport) long __cdecl OtherFunc(int someValue);
Neben leichter zu halten, wenn die Signatur einer exportierten Funktion Änderungen während der Entwicklung, müssen Sie mit Ihrem #define
nicht rumschraube Wrapper.
Nachteil: Wenn sich während der Entwicklung die Anzahl der Bytes in der Parameterliste einer bestimmten Funktion ändert, wird sie nicht von der Anwendung beim Importieren der Funktion abgefangen, da 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 Dinge besser mit C++ - Funktionen (Umhüllen der gesamten explizit geladenen Bibliothek in einer Wrapper-Klasse, z.):
class MyDLLWrapper {
public:
MyDLLWrapper(const std::string& moduleName); // load library here
~MyDLLWrapper(); // free library here
FARPROC WINAPI getProcAddress(const std::string& symbolName) const {
return ::GetProcAddress(m_hModule, decorateSymbolName(symbolName));
}
// etc., etc.
private:
HMODULE m_hModule;
// etc.
// ...
};
Es gibt tatsächlich viel mehr können Sie mit einer Wrapper-Klasse wie dies zu tun, es ist nur ein Beispiel.
Auf edit: da OP mit PInvoke in den Kommentaren erwähnt - wenn jemand dies zu tun entscheidet, vergessen Sie nichtCallingConvention = CallingConvention.Cdecl
in der [DllImport]
Erklärung hinzuzufügen, wenn PInvoke verwenden. __cdecl
ist möglicherweise der Standardwert für nicht verwaltetes C/C++, ist jedoch nicht der Standardwert für verwalteten Code.
Also wenn ich das richtig verstehe, müsste ich das nur zu den dll-projekten hinzufügen und dann Meine Anwendungen und C# PInvoke mit den DLLs funktionieren ohne Änderungen? Wenn ja, gibt es Nachteile gegenüber den anderen vorgeschlagenen Lösungen? – dbostream
Ok ich sehe, danke für die Eingabe. – dbostream
@dbostream: Um Funktionen mit reinen C-Schnittstellen aus nativer C++ - DLL zu exportieren, finde ich .DEF-Dateien praktisch, um _undecorated_ Funktionsnamen zu erhalten. –