2009-06-27 6 views
6

Qual è il modo migliore per progettare un API C per DLL che si occupa del problema del passaggio di "oggetti" che dipendono dal runtime C (FILE *, puntatore restituito da malloc, ecc ...) . Ad esempio, se due DLL sono collegate a una versione diversa del runtime, la mia comprensione è che non è possibile passare un FILE * da una DLL all'altro in modo sicuro.C Oggetti runtime, contorni dll

È l'unica soluzione per utilizzare l'API dipendente da Windows (che è garantito per funzionare su DLL)? L'API C esiste già ed è matura, ma è stata progettata da un POV unix, principalmente (e deve ancora lavorare su unix, ovviamente).

risposta

0

Il problema con i diversi tempi di esecuzione non è risolvibile perché il FILE * struct appartiene una runtime su un sistema Windows.

Ma se si scrive un piccolo wrapper, l'interfaccia è terminata e non fa male.

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

class IFile { 

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

    virtual delete() = 0; 
} 

Si tratta di salvare per essere passato dall'altra parte confini dll ovunque e in realtà non fa male.

P.S .: Prestare attenzione se si inizia a lanciare eccezioni tra i limiti di dll. Questo funzionerà tranquillamente se si soddisfano alcuni creteri di progettazione su Windows OS ma falliranno su alcuni altri.

1

Nessuna delle risposte esistenti è corretta: Dato quanto segue su Windows: si hanno due DLL, ognuna delle quali è collegata staticamente con due diverse versioni delle librerie standard C/C++.

In questo caso, non è necessario passare i puntatori alle strutture create dalla libreria standard C/C++ in una DLL all'altra. Il motivo è che queste strutture possono essere diverse tra le due implementazioni di libreria standard C/C++.

L'altra cosa che non si dovrebbe fare è liberare un puntatore allocato da new o malloc da una DLL allocata nell'altra. Anche il gestore di heap può essere implementato diversamente.

Nota, è possibile utilizzare i puntatori tra le DLL: puntano semplicemente alla memoria. È il libero che è il problema.

Ora, potreste scoprire che funziona, ma se lo fa, allora siete solo fortunati. Questo potrebbe causare problemi in futuro.

Una potenziale soluzione al problema è il collegamento dinamico allo CRT. Ad esempio, è possibile collegare dinamicamente a MSVCRT.DLL. In questo modo le DLL utilizzeranno sempre lo stesso CRT.

Nota, suggerisco che non è una buona pratica passare le strutture di dati CRT tra DLL. Potresti voler vedere se riesci a valutare meglio le cose.

Nota, non sono un esperto di Linux/Unix, ma avrete anche gli stessi problemi su quei sistemi operativi.

+0

Capisco i problemi - sto chiedendo risposte a questo problema :) Speravo in una soluzione che non presupponesse di cambiare l'API C esistente (con FILE * nella firma e così via), ma sembra che non ci sia Se non posso garantire tutto per collegare lo stesso runtime C? Sebbene il problema sia teoricamente lo stesso su Unix, raramente è un problema perché esiste un solo runtime C. Non ho mai avuto un singolo problema passando FILE *, i descrittori di file su Unix tra le librerie - un sacco di C APIS sono progettati in questo modo e funzionano perfettamente su quei sistemi operativi. –

+0

Se avere FILE * nella firma dell'API non è una buona pratica, come si gestiscono i flussi di file? Avete bisogno di esportare le funzioni per aprire e chiudere i file se la DLL che chiamate ha alcune funzioni che chiamano internamente fprintf e altre funzioni che si aspettano FILE *? –

+0

Ho suggerito una soluzione :) Non collegare il CRT in modo statico. collegamento a MSVCRT.DLL. Sarei piuttosto sorpreso se tutte le librerie CRT su Linux, Unix o MAC fossero garantite per funzionare in modo impeccabile. Penso che tu sia stato fortunato anche lì. – Foredecker

0

Se l'API C esiste ed è maturo, ignorando il CRT internamente utilizzando roba pura API Win32 si ottiene metà strada. L'altra metà si sta accertando che l'utente della DLL usi le funzioni API Win32 corrispondenti. Ciò renderà la tua API meno portabile, sia nell'uso che nella documentazione. Inoltre, anche se si va in questo modo con l'allocazione della memoria, dove entrambe le funzioni CRT e quelle Win32 gestiscono con void *, si hanno ancora problemi con il file - l'API Win32 utilizza gli handle e non conosce la struttura FILE.

Non sono sicuro di quali siano le limitazioni del FILE *, ma presumo che il problema sia lo stesso delle allocazioni CRT tra i moduli. MSVCRT utilizza internamente Win32 per gestire le operazioni sui file e l'handle di file sottostante può essere utilizzato da ogni modulo all'interno dello stesso processo. Quello che potrebbe non funzionare è chiudere un file che è stato aperto da un altro modulo, il che comporta la liberazione della struttura FILE su un CRT possibilmente diverso.

Cosa farei, se la modifica dell'API è ancora un'opzione, è possibile esportare le funzioni di pulizia per qualsiasi possibile "oggetto" creato all'interno della DLL. Queste funzioni di pulizia gestiranno lo smaltimento dell'oggetto dato nel modo in cui corrisponde al modo in cui è stato creato all'interno di quella DLL. Ciò renderà la DLL assolutamente portatile in termini di utilizzo. L'unica preoccupazione che avrai allora è assicurarti che l'utente della DLL usi effettivamente le tue funzioni di pulizia piuttosto che quelle normali CRT. Questo può essere fatto usando diversi trucchi, che meritano un'altra domanda ...

2

Hai chiesto una soluzione C, non una C++.

Il metodo usuale (s) per fare questo genere di cose in C sono:

  • design dei moduli API per semplicemente non richiedono oggetti CRT. Ottieni cose passate attraverso tipi di C grezzi - ad esempio, fai in modo che il consumatore carichi il file e ti passi semplicemente il puntatore. Oppure, consente al cliente di passare un nome file completo, che viene aperto, letto e chiuso internamente.

  • Un approccio utilizzato da altri moduli c, l'MS cabinet SD e parti della libreria OpenSSL iirc vengono in mente, ottenere l'applicazione che consuma passare in puntatori alle funzioni per la funzione di inizializzazione. Quindi, qualsiasi API passata a un FILE * a un certo punto durante l'inizializzazione ha preso un puntatore a una struttura con puntatori di funzione che corrispondono alle firme di fread, fopen ecc. Quando si ha a che fare con il FILE esterno * s la dll usa sempre il passato funzioni piuttosto che le funzioni CRT.

Con alcuni semplici trucchi come questo si può fare la vostra interfaccia DLL C del tutto indipendente dalla host CRT - e di fatto richiedono l'esercito ad essere scritto in C o C++ a tutti.