2009-06-27 7 views
6

Was ist der beste Weg, eine C-API für dlls zu entwerfen, die das Problem der Übergabe von C-laufzeitabhängigen Objekten behandelt (FILE *, von malloc zurückgegebener Zeiger usw.) . Wenn zum Beispiel zwei DLLs mit einer anderen Version der Laufzeit verbunden sind, ist es für mich selbstverständlich, dass Sie eine FILE * nicht sicher von einer DLL zur anderen weitergeben können.C Laufzeitobjekte, DLL-Grenzen

Ist die einzige Lösung, Windows-abhängige API zu verwenden (die garantiert über DLLs funktionieren)? Die C-API existiert bereits und ist ausgereift, wurde aber hauptsächlich von einem Unix-POV entworfen (und muss natürlich immer noch unter Unix arbeiten).

Antwort

0

Das Problem mit den verschiedenen Laufzeiten ist nicht lösbar, da die Datei FILE * struct zu einer Laufzeit auf einem Windows-System gehört.

Aber wenn Sie eine kleine Wrapper-Schnittstelle schreiben, ist Ihre getan und es tut nicht wirklich weh.

stdcall IFile* IFileFactory(const char* filename, const char* mode); 

class IFile { 

    virtual fwrite(...) = 0; 
    virtual fread(...) = 0; 

    virtual delete() = 0; 
} 

Dies ist sicher, über dll Grenzen überall weitergegeben werden und nicht wirklich weh tun.

S.S .: Seien Sie vorsichtig, wenn Sie beginnen, Ausnahmen über DLL-Grenzen hinweg zu werfen. Dies funktioniert sehr gut, wenn Sie einige Design-Creterions unter Windows OS ausführen, aber bei einigen anderen nicht.

1

Keine der vorhandenen Antworten ist richtig: Angesichts der folgenden unter Windows: Sie haben zwei DLLs, jede ist statisch mit zwei verschiedenen Versionen der C/C++ - Standardbibliotheken verknüpft.

In diesem Fall sollten Sie keine Zeiger auf Strukturen übergeben, die von der C/C++ - Standardbibliothek in einer DLL zur anderen erstellt wurden. Der Grund ist, dass diese Strukturen zwischen den zwei C/C++ - Standardbibliotheksimplementierungen unterscheiden können.

Die andere Sache, die Sie nicht tun sollten, ist ein Zeiger freigegeben von new oder malloc von einer DLL, die in der anderen zugewiesen wurde. Der Heap-Manager kann auch anders implementiert werden.

Hinweis, Sie können die Zeiger zwischen den DLLs verwenden - sie zeigen nur auf Speicher. Es ist die Freiheit, die das Problem ist.

Jetzt können Sie feststellen, dass dies funktioniert, aber wenn es funktioniert, dann sind Sie nur Glück. Dies wird Ihnen in der Zukunft wahrscheinlich Probleme bereiten.

Eine mögliche Lösung für Ihr Problem ist die dynamische Verknüpfung mit der CRT. Beispielsweise könnten Sie dynamisch mit MSVCRT.DLL verknüpfen. Auf diese Weise verwenden Ihre DLLs immer dieselbe CRT.

Hinweis, ich schlage vor, dass es keine bewährte Methode ist, CRT-Datenstrukturen zwischen DLLs zu übergeben. Vielleicht möchten Sie sehen, ob Sie Dinge besser einteilen können.

Hinweis, ich bin kein Linux/Unix-Experte - aber Sie werden die gleichen Probleme auf diesen Betriebssystemen haben.

+0

Ich verstehe die Probleme - ich frage nach Antworten auf dieses Problem :) Ich hatte auf eine Lösung gehofft, die nicht davon ausgeht, bestehende C-API (mit FILE * in der Unterschrift und so weiter) zu ändern, aber es scheint, dass es nicht Wenn ich nicht garantieren kann, dass alles mit der gleichen C-Laufzeit verknüpft ist? Obwohl das Problem unter Unix theoretisch das gleiche ist, ist es selten ein Problem, da es nur eine C-Laufzeit gibt. Ich hatte nie ein einziges Problem mit FILE *, Dateideskriptoren auf Unix zwischen Bibliotheken - viele C APIS sind auf diese Weise entworfen und funktionieren einwandfrei auf diesen Betriebssystemen. –

+0

Wenn FILE * in der Signatur der API keine gute Praxis ist, wie gehen Sie überhaupt mit Dateiströmen um? Müssen Sie die Funktionen exportieren, um die Dateien zu öffnen und zu schließen, wenn die aufgerufene DLL einige Funktionen hat, die intern fprintf und andere Funktionen aufrufen, die FILE * erwarten? –

+0

Ich habe Vorschlag eine Lösung :) nicht statisch mit dem CRT verknüpfen. Verbindung zu MSVCRT.DLL herstellen. Ich wäre ziemlich überrascht, wenn alle CRT-Bibliotheken unter Linux, Unix oder MAC einwandfrei funktionieren würden. Ich denke du warst auch dort glücklich. – Foredecker

0

Wenn die C-API existiert und ausgereift ist, umgehen Sie den CRT intern, indem Sie reines Win32-API-Zeug verwenden, um den halben Weg zu gehen. Die andere Hälfte stellt sicher, dass der DLL-Benutzer die entsprechenden Win32-API-Funktionen verwendet. Dadurch wird Ihre API sowohl in der Verwendung als auch in der Dokumentation weniger portierbar. Auch wenn Sie diesen Weg mit Speicherzuweisung gehen, wo sowohl die CRT-Funktionen als auch die Win32-Funktionen mit void * umgehen, haben Sie immer noch Probleme mit dem Dateikram - die Win32-API verwendet Handles und weiß nichts über die FILE-Struktur.

Ich bin nicht ganz sicher, was die Einschränkungen der Datei * sind, aber ich nehme an, das Problem ist das gleiche wie bei CRT-Zuordnungen über Module hinweg. MSVCRT verwendet Win32 intern, um die Dateivorgänge zu verarbeiten, und das zugrunde liegende Dateihandle kann von jedem Modul innerhalb desselben Prozesses verwendet werden. Was nicht funktioniert, ist das Schließen einer Datei, die von einem anderen Modul geöffnet wurde, was das Freigeben der FILE-Struktur auf einer möglicherweise anderen CRT beinhaltet.

Was ich tun würde, wenn das Ändern der API noch eine Option ist, ist Exportbereinigungsfunktionen für jedes mögliche "Objekt", das innerhalb der DLL erstellt wird. Diese Bereinigungsfunktionen werden die Entsorgung des gegebenen Objekts auf die Weise behandeln, die der Art entspricht, wie es in dieser DLL erstellt wurde. Dies wird auch die DLL in Bezug auf die Verwendung absolut portabel machen. Die einzige Sorge, die Sie dann haben, ist sicherzustellen, dass der Benutzer der DLL tatsächlich Ihre Bereinigungsfunktionen anstelle der normalen CRT-Funktionen verwendet. Dies kann mit mehreren Tricks gemacht werden, die eine andere Frage verdienen ...

2

Sie haben nach einer C-, nicht nach einer C++ - Lösung gefragt.

Die übliche Methode (n) für das Tun diese Art der Sache in C:

  • Entwurf der Module API einfach nicht CRT Objekte benötigen. Erhalten Sie Zeug in rohen C-Typen übergangen - d. H. Den Verbraucher zum Laden der Datei und übergeben Sie einfach den Zeiger. Oder lassen Sie den Benutzer einen vollständig qualifizierten Dateinamen übergeben, der intern geöffnet, gelesen und geschlossen wird.

  • Ein Ansatz, der von anderen c-Modulen verwendet wird, der MS-Cabinet-SD und Teile der OpenSSL-Bibliothek iirc in den Sinn kommen, die verbrauchende Anwendung dazu bringen, Zeiger auf Funktionen an die Initialisierungsfunktion zu übergeben. Jede API, an die Sie ein FILE * übergeben, würde zu irgendeinem Zeitpunkt während der Initialisierung einen Zeiger auf eine Struktur mit Funktionszeigern haben, die den Signaturen von fread, fopen usw. entsprechen. Bei den externen FILE * s verwendet die DLL immer das übergebene Funktionen und nicht die CRT-Funktionen.

Mit einigen einfachen Tricks wie diese können Sie Ihren C-DLLs Schnittstelle völlig unabhängig von dem Host-CRT machen - oder in der Tat erfordern die Host geschrieben wird in C oder C++ überhaupt.