Regole e limitazioni generali
Sezione specifica Microsoft
Se si dichiara una funzione o un oggetto senza l'attributo dllimport o dllexport, la funzione o l'oggetto non viene considerato parte dell'interfaccia DLL. Di conseguenza, la definizione della funzione o dell'oggetto deve essere presente in tale modulo o in un altro modulo dello stesso programma. Per rendere la funzione o l'oggetto parte dell'interfaccia DLL, è necessario dichiararne la definizione nell'altro modulo come dllexport. In caso contrario, viene generato un errore del linker.
Se si dichiara una funzione o un oggetto con l'attributo dllexport, la relativa definizione deve trovarsi in un modulo dello stesso programma. In caso contrario, viene generato un errore del linker.
Se un unico modulo nel programma contiene entrambe le dichiarazioni dllimport e dllexport per la stessa funzione o oggetto, l'attributo dllexport ha la precedenza sull'attributo dllimport. Viene tuttavia generato un avviso del compilatore. Ad esempio:
__declspec( dllimport ) int i; __declspec( dllexport ) int i; // Warning; inconsistent; // dllexport takes precedence.
In C++ è possibile inizializzare un puntatore ai dati locale dichiarato a livello globale o statico oppure con l'indirizzo di un oggetto dati dichiarato con l'attributo dllimport, procedura che genera un errore in C. Inoltre, è possibile inizializzare un puntatore a funzione locale statico con l'indirizzo di una funzione dichiarata con l'attributo dllimport. In C tale assegnazione imposta il puntatore sull'indirizzo del thunk di importazione della DLL (uno stub di codice che trasferisce il controllo alla funzione) anziché sull'indirizzo della funzione. In C++ imposta il puntatore sull'indirizzo della funzione. Ad esempio:
__declspec( dllimport ) void func1( void ); __declspec( dllimport ) int i; int *pi = &i; // Error in C static void ( *pf )( void ) = &func1; // Address of thunk in C, // function in C++ void func2() { static int *pi = &i; // Error in C static void ( *pf )( void ) = &func1; // Address of thunk in C, // function in C++ }
Tuttavia, poiché un programma che include l'attributo dllexport nella dichiarazione di un oggetto deve fornire la definizione di tale oggetto in qualche punto del programma, è possibile inizializzare un puntatore a funzione statico globale o locale con l'indirizzo di una funzione dllexport. Analogamente, è possibile inizializzare un puntatore a dati statico globale o locale con l'indirizzo di un oggetto dati dllexport. Ad esempio, il seguente codice non genera errori in C o in C++:
__declspec( dllexport ) void func1( void ); __declspec( dllexport ) int i; int *pi = &i; // Okay static void ( *pf )( void ) = &func1; // Okay void func2() { static int *pi = &i; // Okay static void ( *pf )( void ) = &func1; // Okay }
A causa di una modifica nel comportamento in Visual C++ .NET, finalizzata a rendere l'applicazione di dllexport più coerente tra le classi normali e le specializzazioni dei modelli di classe, se si applica dllexport a una classe normale che ha una classe base non contrassegnata come dllexport, il compilatore genererà l'avviso C4275.
Il compilatore genera lo stesso avviso se la classe base è una specializzazione di un modello di classe. Per risolvere questo problema, contrassegnare la classe base con dllexport. Il problema con la specializzazione di un modello di classe è rappresentato da dove posizionare __declspec(dllexport); il modello di classe non può essere contrassegnato. È invece possibile creare in modo esplicito un'istanza del modello di classe e contrassegnare questa creazione esplicita con dllexport. Ad esempio:
template class __declspec(dllexport) B<int>; class __declspec(dllexport) D : public B<int> { // ...
Questa soluzione alternativa non riesce se un argomento del modello è la classe di derivazione. Ad esempio:
class __declspec(dllexport) D : public B<D> { // ...
Trattandosi di una situazione comune con i modelli, il compilatore modifica la semantica di dllexport quando viene applicato a una classe che include una o più classi base e quando una o più classi base sono una specializzazione del modello di classe. In questo caso, il compilatore applica in modo implicito dllexport alle specializzazioni dei modelli della classe. In Visual C++ .NET un utente può eseguire le operazioni seguenti senza ricevere un avviso:
class __declspec(dllexport) D : public B<D> { // ...