Domanda

Questa domanda è collegata a " Come rendere coerente binari DLL tra le versioni VS? "

  • Sono state create applicazioni e DLL con VC6 e una nuova applicazione costruita con VC9. L'app VC9 deve essere utilizzata DLL compilate con VC6, la maggior parte di che sono scritti in C e uno in C ++.
  • La libreria C ++ è problematica a causa di problemi di decorazione / manomissione del nome.
  • Compilare tutto con VC9 è attualmente non è un'opzione come lì sembrano essere alcuni effetti collaterali. Risolvere questi sarebbe abbastanza tempo consumando.
  • Posso modificare la libreria C ++, tuttavia deve essere compilata con VC6.
  • La libreria C ++ è essenzialmente un OO-wrapper per un'altra libreria C. L'app VC9 utilizza alcune funzioni statiche e alcune non statiche.

Mentre le funzioni statiche possono essere gestite con qualcosa del genere

// Header file
class DLL_API Foo
{
    int init();
}

extern "C"
{
    int DLL_API Foo_init();
}

// Implementation file
int Foo_init()
{
    return Foo::init();
}

non è così facile con i metodi non statici.

A quanto ho capito, Chris Becke's di usare un'interfaccia simile a COM non mi aiuterà perché i nomi dei membri dell'interfaccia saranno comunque decorati e quindi inaccessibili da un binario creato con un compilatore diverso. Sono proprio lì?

L'unica soluzione sarebbe quella di scrivere un'interfaccia DLL in stile C usando i gestori sugli oggetti o mi sto perdendo qualcosa? In quel caso, immagino, probabilmente avrei meno sforzi nell'usare direttamente la libreria C incartata.

È stato utile?

Soluzione

I nomi dei membri dell'interfaccia non saranno decorati - sono solo offset in una vtable. Puoi definire un'interfaccia (usando una C-struct, piuttosto che una COM " interface ") in un file di intestazione, quindi:

struct IFoo {
    int Init() = 0;
};

Quindi, è possibile esportare una funzione dalla DLL, senza alterazioni:

class CFoo : public IFoo { /* ... */ };
extern "C" IFoo * __stdcall GetFoo() { return new CFoo(); }

Funzionerà bene, a condizione che tu stia utilizzando un compilatore che genera vtables compatibili. Microsoft C ++ ha generato lo stesso formato vtable da (almeno, credo) MSVC6.1 per DOS, dove vtable è un semplice elenco di puntatori a funzioni (con thunking nel caso di ereditarietà multipla). GNU C ++ (se ricordo bene) genera vtables con puntatori a funzione e offset relativi. Questi non sono compatibili tra loro.

Altri suggerimenti

Il problema più grande da considerare quando si utilizza una DLL compilata con un compilatore C ++ diverso rispetto al file EXE chiamante è l'allocazione di memoria e la durata degli oggetti.

Suppongo che tu possa superare il nome mangling (e chiamando convention), il che non è difficile se usi un compilatore con mangling compatibile (penso che VC6 sia ampiamente compatibile con VS2008) o se usi extern " C ".

Il punto in cui si verificano problemi è quando si alloca qualcosa utilizzando new (o malloc ) dalla DLL, quindi si restituisce questo al chiamante. delete (o free ) del chiamante tenterà di liberare l'oggetto da un heap diverso. Questo andrà terribilmente storto.

Puoi fare una cosa IFoo :: Release in stile COM o una cosa MyDllFree () . Entrambi, poiché richiamano nella DLL, utilizzeranno la corretta implementazione di delete (o free () ), quindi elimineranno l'oggetto corretto.

In alternativa, puoi assicurarti di utilizzare LocalAlloc (ad esempio), in modo che EXE e DLL utilizzino lo stesso heap.

Beh, penso Il suggerimento di Chris Becke va bene. Non userei La prima soluzione di Roger , che utilizza un'interfaccia solo di nome e, come accenna, può incorrere in problemi di incompatibilità nella gestione dei compilatori di classi astratte e metodi virtuali. Roger sottolinea l'attraente caso coerente della COM in il suo follow-on .

  1. Il punto dolente: è necessario imparare a fare richieste di interfaccia COM e gestire correttamente IUnknown, basandosi almeno su IUnknown: AddRef e IUnknown: Release. Se le implementazioni di interfacce possono supportare più di un'interfaccia o se i metodi possono anche restituire interfacce, potrebbe essere necessario familiarizzare con IUnknown: QueryInterface.

  2. Ecco l'idea chiave. Tutti i programmi che utilizzano l'implementazione dell'interfaccia (ma non la implementano) usano un #include " *. H " comune file che definisce l'interfaccia come una struttura (C) o una classe C / C ++ (VC ++) o struct (non VC ++ ma C ++). Il file * .h si adatta automaticamente in modo appropriato a seconda che si stia compilando un programma in linguaggio C o un programma in linguaggio C ++. Non devi conoscere quella parte semplicemente per usare il file * .h. Quello che fa il file * .h è definire la struttura o il tipo di interfaccia, diciamo, IFoo, con le sue funzioni di membro virtuale (e solo funzioni, nessuna visibilità diretta ai membri di dati in questo approccio).

  3. Il file di intestazione è costruito per onorare lo standard binario COM in un modo che funziona per C e che funziona per C ++ indipendentemente dal compilatore C ++ utilizzato. (Il folk Java JNI l'ha capito.) Ciò significa che funziona tra moduli compilati separatamente di qualsiasi origine, purché una struttura costituita interamente da puntatori di immissione di funzioni (una vtable) sia mappata nella memoria allo stesso modo da tutti loro (quindi devono essere tutti x86 a 32 bit, o tutti x64, per esempio).

  4. Nella DLL che implementa l'interfaccia COM tramite una classe wrapper di qualche tipo, è necessario solo un punto di ingresso di fabbrica. Qualcosa come un

    extern "C" HRESULT MkIFooImplementation (void ** ppv);

che restituisce un HRESULT (è necessario conoscere anche quelli) e restituirà anche un * pv in una posizione fornita per ricevere il puntatore all'interfaccia IFoo. (Sto scremando e ci sono dettagli più accurati di cui avrai bisogno qui. Non fidarti della mia sintassi) L'effettivo stereotipo di funzione che usi per questo è anche dichiarato nel file * .h.

  1. Il punto è che la voce di fabbrica, che è sempre una decorazione esterna "C" quotata; esegue tutta la creazione della classe wrapper necessaria e quindi fornisce un puntatore all'interfaccia Ifoo nella posizione specificata. Ciò significa che tutta la gestione della memoria per la creazione della classe e tutta la gestione della memoria per la sua finalizzazione, ecc., Avverrà nella DLL in cui viene creato il wrapper. Questo è l'unico posto dove devi occuparti di quei dettagli.

  2. Quando si ottiene un risultato OK dalla funzione di fabbrica, è stato emesso un puntatore all'interfaccia ed è già stato riservato per te (esiste un'operazione implicita IFoo: Addref già eseguita per conto del puntatore dell'interfaccia sono stati consegnati).

  3. Quando hai finito con l'interfaccia, la rilasci con una chiamata sul metodo IFoo: Release dell'interfaccia. È l'implementazione della versione finale (nel caso in cui tu abbia fatto più copie AddRef'd) che ridurrà la classe e il suo supporto di interfaccia nella DLL di fabbrica. Questo è ciò che ti fa affidamento corretto su un'allocazione e un rilascio dinamici consistenti degli storni dietro l'interfaccia

Ci sono anche altre cose da considerare, come ad esempio i runtime utilizzati dalle varie librerie. Se non vengono condivisi oggetti, va bene, ma sembra alquanto improbabile a prima vista.
I suggerimenti di Chris Becker sono piuttosto precisi: l'utilizzo di un'interfaccia COM effettiva può aiutarti a ottenere la compatibilità binaria di cui hai bisogno. Il chilometraggio può variare :)

non è divertente, amico. ti senti molto frustrato, probabilmente dovresti dare questo:

  

Sarebbe l'unica soluzione per scrivere a   Interfaccia DLL di tipo C che utilizza gestori   agli oggetti o mi sto perdendo   qualcosa? In quel caso, immagino, io   probabilmente avrebbe meno sforzo con   usando direttamente la libreria C incartata.

uno sguardo da vicino. buona fortuna.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top