DLL für verschiedene Programmierumgebungen
-
Hallo zusammen,
ich habe ein seltsames Problem und würde mich sehr über Ihre Hilfe freuen. Ich habe eine multithreading dll mit shared memory Benutzung in MS VC++ 6 geschrieben, die in möglichst vielen Programmierumgebungen benutzbar sein sollte. Deswegen wurde als Aufrufkonvention stdcall benutzt und die erweiterten Namen mit extern "C" und einer DEF-Datei wieder in sinnvolle verwandelt. Das funktioniert prima mit Delphi, Visual Basic und LabView.
Jetzt will ich Beispiele in C/C++ anbinden und stolpere ausgerechnet dort darüber wo ich die dll entwickelt habe. Das Problem ist die Namenserweiterung, die ich beim Einbinden der Importbibliothek ("MyDLL.lib") nicht aufgelöst bekomme. Meine Funktionen werden z.B. als "MyFunction" exportiert und sind auch so in der Importbibliothek verzeichnet. VC++ weigert sich jetzt jedoch hartnäckig mich auf diese nicht-erweiterten Funktionsnamen zugreifen zu lassen. "MyEXE.obj : error LNK2001" heißt es da ständig und "unresolved external symbol" und je nachdem mit welcher Methode ich es einzubinden versuche, werden dann noch die üblichen erweiterten Namen passend zu der Methode hinten angehängt (extern "C" sucht also z.B. nach "_MyFunction@8", __declspec(dllimport) nach einem stdcall-üblich kryptischen Namen wie "__imp_?MyFunction@@YGNN@Z"). Der legendäre Jeffrey Richter verspricht, VC++ würde mit __declspec(dllimport) grundsätzlich auch die nicht-erweiterten Namen finden und korrekt adressieren. Das war jedoch nichts!
Kann mir hierbei jemand helfen? Das wäre genial. Ich blicke da langsam nach irrinnig vielen Seiten Lektüre nicht mehr durch.
Beispiel:
---------
Datei MyDLL.def:LIBRARY "MyDLL.DLL" DESCRIPTION 'MYDLL.DLL V3.0.1.9 29-01-04' EXPORTS MyFunction = _MyFunction@8 @1 ...Datei MyDLL.h:
#ifdef My_EXPORTS #define My_API extern "C" // so wurde die // dll erstellt #else #define My_API __declspec(dllimport) #endif My_API double MyFunction(double P) ; ...Die Importbibliothek ist, so ich das beurteilen kann, richtig in "Settings" im "Link"-Register eingetragen. Die Aufrufkonvention ist stdcall in der dll und in meinem Test-Projekt (kein mfc). Ich habe die Release- und Debug-Konfigurationen durchprobiert. Was mache ich da falsch?
Es kann doch nicht sein, daß ich alle Funktionen dynamisch laden muß.
Ich sage jetzt schon mal ein riesiges Dankeschön für jede Hilfe und alle Hilfeversuche!
Stefan.
-
Wenn Du extern "C" exportierst, mußt Du auch so importieren. Der Linker suchst nach einer CPP-Funktion, und eine solche gibt's natürlich nicht. Alternativ kannst du Dein Quellfile auch in C ändern, dann geht es auch wieder.
-
Vielen Dank, -King-, für die Anregung, aber das habe ich ja schon gemacht. Da findet er dann nur "_MyFunction@8" nicht, statt "__imp_?MyFunction@@YGNN@Z" wie mit __declspec(dllimport).
Also, aus irgendeinem Grunde wird einfach nur nicht auf die Namensdeklarationen in MyDLL.lib eingegangen. Der Grund hängt sicher damit zusammen, daß der Linker irgendwie nicht die Kurve von seinen standardmäßig erweiterten Namen zu den nicht-erweiterten kriegt. Möglicherweise einfach nur eine falsche Einstellung von mir, aber ich habe keine Idee mehr, welche bzw. wo.
Gibt es eine Möglichkeit, den Linker zu zwingen, sich auch beim Importieren auf nicht-erweiterte Namen zu konzentrieren (ähnlich der .DEF-Datei beim Exportieren)?
-
Mich verwirrt noch das Exportieren/ Importiern an sich. Von __stdcall sehe ich irgendwie noch gar nichts. Die Import Library hast Du aber dem Linker hinzugefügt?
// MyDLL.h #include <windows.h> #ifndef DLLEXPORT #define DLLEXPORT __declspec(dllexport) #endif #ifndef DLLIMPORT #ifdef MY_EXPORTS #define DLLIMPORT __declspec(dllexport) #else #define DLLIMPORT __declspec(dllimport) #endif #endif #ifndef STDCALL #define STDCALL __stdcall #endif #define My_API EXTERN_C DLLIMPORT My_API double STDCALL MyFunction(double P);
-
Die Aufrufkonvention kann man im Projekt-Einstellungsdialog als Standard vordefinieren, so daß das nicht bei jeder einzelnen Prozedurdeklaration wiederholt werden muß. Dort ist __stdcall eingestellt statt __cdecl.
Auch die Importlibrary ist dort gesetzt.
// MyDLL.h #include <windows.h> #ifndef DLLIMPORT #define DLLIMPORT __declspec(dllimport) #endif // ich habe alle Kombinationen durchprobiert, die Fehlermeldung bleibt, // nur das gesuchte Symbol variiert: // // Definition // gesuchtes Symbol // #define My_API EXTERN_C DLLIMPORT // __imp__MyFunction@8 //#define My_API EXTERN_C // _MyFunction@8 //#define My_API DLLIMPORT // __imp_?MyFunction@@YGNN@Z //#define My_API // ?MyFunction@@YGNN@Z // __stdcall muß nicht mehr mit angegeben werden My_API double MyFunction(double P);Das sieht ja alles ganz logisch aus, nur werden nicht die Varianten aus der lib getroffen, die da wären: "MyFunction" in Bezug auf __IMPORT_DESCRIPTOR_MyDLL und "__imp_MyFunction" in Bezug auf __NULL_IMPORT_DESCRIPTOR. Immerin habe ich ja das erste Underscore und "@8" der weiten Variante auch nur mit Hilfe der DEF-Datei entfernen können (zumindest bei __stdcall).
Nur um zu schauen, was passiert, habe ich auch die Standardaufrufkonvention zwischenzeitlich zurück auf __cdecl gesetzt, die dabei gesuchten Symbole treffen's dann auch fast: "_MyFunction" war das beste, aber auf dem übrigen Underscore beharrt er. Macht nichts, ich kann ja mit __cdecl sowieso nichts anfangen.
-
Sag mal, King, dar ich Dir mal .h, .lib und .dll schicken?
-
St.B. schrieb:
Die Aufrufkonvention kann man im Projekt-Einstellungsdialog als Standard vordefinieren, so daß das nicht bei jeder einzelnen Prozedurdeklaration wiederholt werden muß. Dort ist __stdcall eingestellt statt __cdecl.
Genau, man kann das verstellen. Genau deswegen solltest Du das immer dazuschreiben, dann können Dir die Einstellungen egal sein. Sonst wird die Anwendung Deines Headers/ Deiner DLL zum reinsten Glücksspiel. Außerdem: Sicher ist sicher, und Sicherheit geht vor.
// Nur das ist richtig, da muß nicht probiert werden #define My_API EXTERN_C DLLIMPORT // __imp__MyFunction@8Vielleicht solltest Du mal den internen Namen im EXPORTS-Abschnitt des DEF-Files weglassen:
EXPORTS MyFunction... und fertig.
-
St.B. schrieb:
Sag mal, King, dar ich Dir mal .h, .lib und .dll schicken?
Wenn's nicht gar zu viel ist, dann los.

-
Also ich löse das Problem folgendermassen:
#define export(ret) extern "C" ret _stdcall __declspec(dllexport) export(void)foo1(){...} export(char*)foo2(){...}Und dann zum importiren:
#define import(ret) extern "C" ret _stdcall __declspec(dllimport) import(void)foo1(){...} import(char*)foo2(){...}Ich benutze Bc5.5 und der schluck das einbahnfrei ohne irgendwelche compiler switches.
-
Hallo zusammen,
mein Problem ist gelöst. Soll heißen: King hat es gelöst! Riesigen Dank auch an dieser Stelle nochmal!
Vielen Dank auch Dir, "Irgendwer"! Es entspricht dem was Du geschrieben hast.
Für den Fall, daß nochmal jemand über dieses Problem stolpert und auf der Suche zufällig hier hinein gerät, schreibe ich hier noch eine kurze Zusammenfassung der Lösung:
Das Problem hat sich entpuppt als ein Fehler beim Exportieren. Ich hatte zwar die Aufrufkonvention "__stdcall" in den Projekt-Einstellungen korrekt angegeben, dies wirkte sich zwar richtig, jedoch nur auf den erstellten Code aus, nicht aber auf die exportierten Symbolnamen der Importbibliothek. Die Importbibliothek war "__cdecl"-like geblieben. Das hat sich aufgelöst, nachdem die Aufrufkonvention nochmal explizit zu jeder einzelnen Deklaration angegeben wurde und dann auch wieder "__declspec(dllexport)" zusammen mit "extern "C"" Anwendung gefunden hat. Zum Einbinden der dll kommt somit auch das dazugehörige "extern "C" __declspec(dllimport)" zum Zuge. Im übrigen konnte ich daraufhin auch auf Angabe des erweiterten Namens in der DEF-Datei verzichten (oder mußte es sogar?), was ein extra Zuckerl hierbei ist.
Fazit: Die Aufrufkonvention in den Projekt-Einstellungen (resp. Compiler-Switch) allein genügt nicht! Zumindest nicht in MS VC++ 6!
In Anlehnung an King und "Irgendwer" ergibt sich z.B. folgende Header-Datei:
#ifdef My_EXPORTS #define DLL_IM_EXPORT dllexport #else #define DLL_IM_EXPORT dllimport #endif #define My_API(ret) extern "C" __declspec(DLL_IM_EXPORT) ret __stdcall My_API(double) MyFunction(double P) ; ...Viele Grüße,
Stefan.