TN033: versione DLL di MFC
Questa nota descrive come usare MFCxx.DLL
e MFCxxD.DLL
(dove xx è il numero di versione MFC) librerie di collegamento dinamico condiviso con applicazioni MFC e DLL di estensione MFC. Per altre informazioni sulle normali DLL MFC, vedere Uso di MFC come parte di una DLL.
Questa nota tecnica illustra tre aspetti delle DLL. Gli ultimi due sono per gli utenti più avanzati:
Come creare un'applicazione MFC che usa la versione DLL di MFC
Implementazione delle librerie a collegamento dinamico condiviso MFC
Se si è interessati a creare una DLL con MFC che può essere usata con applicazioni non MFC (note come dll MFC normali), vedere la nota tecnica 11.
Panoramica del supporto di MFCxx.DLL: terminologia e file
DLL MFC normale: si usa una NORMALE DLL MFC per compilare una DLL autonoma usando alcune classi MFC. Le interfacce oltre il limite app/DLL sono interfacce "C" e l'applicazione client non deve essere un'applicazione MFC.
Le DLL MFC regolari sono la versione delle DLL supportate in MFC 1.0. Sono descritti nella nota tecnica 11 e nell'esempio DLLScreenCap
di concetti avanzati MFC.
Nota
A partire da Visual C++ versione 4.0, il termine USRDLL è obsoleto ed è stato sostituito da una normale DLL MFC che collega staticamente a MFC. È anche possibile compilare una normale DLL MFC che collega in modo dinamico a MFC.
MFC 3.0 (e versioni successive) supporta le normali DLL MFC con tutte le nuove funzionalità, incluse le classi OLE e Database.
AFXDLL: detta anche versione condivisa delle librerie MFC. È il nuovo supporto della DLL aggiunto in MFC 2.0. La libreria MFC stessa si trova in una serie di DLL (descritte di seguito). Un'applicazione client o una DLL collega dinamicamente le DLL necessarie. Le interfacce attraverso il limite dell'applicazione/DLL sono interfacce di classe C++/MFC. L'applicazione client DEVE essere un'applicazione MFC. Questa DLL supporta tutte le funzionalità MFC 3.0 (eccezione: UNICODE non è supportato per le classi di database).
Nota
A partire da Visual C++ versione 4.0, questo tipo di DLL viene definito "DLL di estensione".
Questa nota verrà usata MFCxx.DLL
per fare riferimento all'intero set di DLL MFC, che include:
Debug:
MFCxxD.DLL
(combinato) eMFCSxxD.LIB
(statico).Versione:
MFCxx.DLL
(combinata) eMFCSxx.LIB
(statica).Debug Unicode:
MFCxxUD.DLL
(combinato) eMFCSxxD.LIB
(statico).Versione Unicode:
MFCxxU.DLL
(combinata) eMFCSxxU.LIB
(statica).
Nota
Le MFCSxx[U][D].LIB
librerie vengono usate insieme alle DLL condivise MFC. Queste librerie contengono codice che deve essere collegato in modo statico all'applicazione o alla DLL.
Un'applicazione è collegata alle librerie di importazione corrispondenti:
Debug:
MFCxxD.LIB
Rilascio:
MFCxx.LIB
Debug Unicode:
MFCxxUD.LIB
Versione Unicode:
MFCxxU.LIB
Una DLL di estensione MFC è una DLL che estende MFCxx.DLL
(o altre DLL condivise MFC). In questo caso, viene avviata l'architettura dei componenti MFC. Se si deriva una classe utile da una classe MFC o si compila un altro toolkit simile a MFC, è possibile inserirla in una DLL. La DLL usa MFCxx.DLL
, come fa l'applicazione client finale. Una DLL di estensione MFC consente classi foglia riutilizzabili, classi di base riutilizzabili e classi di visualizzazione e documenti riutilizzabili.
Vantaggi e svantaggi
Perché usare la versione condivisa di MFC
L'uso della libreria condivisa può comportare applicazioni più piccole. Un'applicazione minima che usa la maggior parte della libreria MFC è inferiore a 10.000.
La versione condivisa di MFC supporta dll di estensione MFC e dll MFC normali.
È più veloce compilare un'applicazione che usa le librerie MFC condivise rispetto a un'applicazione MFC collegata staticamente. Questo perché non è necessario collegare MFC stesso. È particolarmente vero nelle compilazioni in
DEBUG
cui il linker deve compattare le informazioni di debug. Quando l'applicazione viene collegata a una DLL che contiene già le informazioni di debug, è necessario compattare le informazioni di debug.
Perché non usare la versione condivisa di MFC:
- La spedizione di un'applicazione che usa la libreria condivisa richiede la spedizione
MFCxx.DLL
e altre librerie con il programma.MFCxx.DLL
è liberamente ridistribuibile come molte DLL, ma è comunque necessario installare la DLL nel programma edizione Standard TUP. Inoltre, dovrai spedire le altre librerie ridistribuibili usate sia dal programma che dalle DLL MFC stesse.
Come scrivere una DLL di estensione MFC
Una DLL di estensione MFC è una DLL che contiene classi e funzioni per espandere le funzionalità delle classi MFC. Una DLL di estensione MFC usa le DLL MFC condivise nello stesso modo in cui un'applicazione le usa, con alcune considerazioni aggiuntive:
Il processo di compilazione è simile alla compilazione di un'applicazione che usa le librerie MFC condivise con alcune opzioni aggiuntive del compilatore e del linker.
Una DLL di estensione MFC non ha una
CWinApp
classe derivata da .Una DLL di estensione MFC deve fornire un oggetto speciale
DllMain
. AppWizard fornisce unaDllMain
funzione che è possibile modificare.Una DLL di estensione MFC fornisce in genere una routine di inizializzazione per creare un
CDynLinkLibrary
oggetto , se la DLL dell'estensione MFC esportaCRuntimeClass
tipi o risorse nell'applicazione. Una classe derivata diCDynLinkLibrary
può essere usata se i dati per applicazione devono essere gestiti dalla DLL dell'estensione MFC.
Queste considerazioni sono descritte in modo più dettagliato di seguito. Fare riferimento anche all'esempio DLLHUSK
MFC Advanced Concepts . Illustra come:
Creare un'applicazione usando le librerie condivise. (
DLLHUSK.EXE
è un'applicazione MFC che collega dinamicamente alle librerie MFC e ad altre DLL.Compilare una DLL di estensione MFC. Mostra in che modo vengono usati flag speciali, ad
_AFXEXT
esempio quando si compila una DLL di estensione MFC.Compilare due esempi di DLL di estensione MFC. Uno mostra la struttura di base di una DLL di estensione MFC con esportazioni limitate (TESTDLL1) e l'altra mostra l'esportazione di un'intera interfaccia di classe (TESTDLL2).
Sia l'applicazione client che qualsiasi DLL di estensione MFC devono usare la stessa versione di MFCxx.DLL
. Seguire le convenzioni delle DLL MFC e fornire sia una versione di debug che di versione (/release
) della DLL dell'estensione MFC. Questa procedura consente ai programmi client di compilare versioni di debug e di rilascio delle applicazioni e collegarle con la versione di debug o versione appropriata di tutte le DLL.
Nota
Poiché i problemi di mangling ed esportazione del nome C++, l'elenco di esportazione da una DLL di estensione MFC può essere diverso tra le versioni di debug e di rilascio della stessa DLL e DLL per piattaforme diverse. La versione MFCxx.DLL
ha circa 2000 punti di ingresso esportati. Il debug MFCxxD.DLL
ha circa 3000 punti di ingresso esportati.
Nota rapida sulla gestione della memoria
La sezione intitolata "Gestione memoria", alla fine di questa nota tecnica, descrive l'implementazione di MFCxx.DLL
con la versione condivisa di MFC. Le informazioni necessarie per implementare solo una DLL di estensione MFC sono descritte qui.
MFCxx.DLL
e tutte le DLL di estensione MFC caricate nello spazio indirizzi di un'applicazione client useranno lo stesso allocatore di memoria, il caricamento delle risorse e altri stati "globali" MFC come se fossero nella stessa applicazione. È significativo perché le librerie DLL non MFC e le normali DLL MFC collegate in modo statico a MFC eseguono esattamente l'opposto: ogni DLL alloca fuori dal proprio pool di memoria.
Se una DLL di estensione MFC alloca memoria, tale memoria può essere liberamente intermixato con qualsiasi altro oggetto allocato dall'applicazione. Inoltre, se un'applicazione che usa le librerie MFC condivise si arresta in modo anomalo, il sistema operativo mantiene l'integrità di qualsiasi altra applicazione MFC che condivide la DLL.
Analogamente, altri stati MFC "globali", ad esempio il file eseguibile corrente da cui caricare le risorse, vengono condivisi anche tra l'applicazione client, tutte le DLL di estensione MFC e MFCxx.DLL
se stesso.
Compilazione di una DLL di estensione MFC
È possibile usare AppWizard per creare un progetto DLL di estensione MFC e genera automaticamente le impostazioni del compilatore e del linker appropriate. Genera anche una DllMain
funzione che è possibile modificare.
Se si converte un progetto esistente in una DLL di estensione MFC, iniziare con le impostazioni standard compilate usando la versione condivisa di MFC. Apportare quindi le modifiche seguenti:
Aggiungere
/D_AFXEXT
ai flag del compilatore. Nella finestra di dialogo Proprietà progetto selezionare la categoria Del preprocessore C/C++>. Aggiungere_AFXEXT
al campo Definisci macro, separando ognuno degli elementi con punto e virgola.Rimuovere l'opzione del
/Gy
compilatore. Nella finestra di dialogo Proprietà progetto selezionare la categoria Generazione codice C/C++>. Assicurarsi che la proprietà Enable Function-Level Linking non sia abilitata. Questa impostazione semplifica l'esportazione delle classi, perché il linker non rimuoverà le funzioni senza riferimenti. Se il progetto originale ha compilato una NORMALE DLL MFC collegata in modo statico a MFC, modificare l'opzione del/MT
compilatore (o/MTd
) in/MD
(o/MDd
).Creare una libreria di esportazione con l'opzione
/DLL
LINK. Questa opzione viene impostata quando si crea una nuova destinazione e si specifica La libreria di collegamento dinamico Win32 come tipo di destinazione.
Modifica dei file di intestazione
L'obiettivo consueto di una DLL di estensione MFC è quello di esportare alcune funzionalità comuni in una o più applicazioni che possono usare tale funzionalità. Essenzialmente, la DLL esporta classi e funzioni globali per l'uso da parte delle applicazioni client.
Per assicurarsi che ogni funzione membro venga contrassegnata per l'importazione o l'esportazione in base alle esigenze, usare le dichiarazioni __declspec(dllexport)
speciali e __declspec(dllimport)
. Quando le applicazioni client usano le classi, si vuole che vengano dichiarate come __declspec(dllimport)
. Quando la DLL dell'estensione MFC stessa viene compilata, le funzioni devono essere dichiarate come __declspec(dllexport)
. La DLL compilata deve anche esportare le funzioni, in modo che i programmi client possano associarli in fase di caricamento.
Per esportare l'intera classe, usare AFX_EXT_CLASS
nella definizione della classe . Il framework definisce questa macro come __declspec(dllexport)
quando _AFXDLL
e _AFXEXT
viene definito, ma lo definisce come __declspec(dllimport)
quando _AFXEXT
non viene definito. _AFXEXT
viene definito solo quando si compila la DLL dell'estensione MFC. Ad esempio:
class AFX_EXT_CLASS CExampleExport : public CObject
{ /* ... class definition ... */ };
Non esportazione dell'intera classe
A volte è possibile esportare solo i singoli membri necessari della classe. Ad esempio, se si esporta una CDialog
classe derivata da , potrebbe essere necessario esportare solo il costruttore e la DoModal
chiamata. È possibile esportare questi membri usando il file DEF della DLL, ma è anche possibile usare AFX_EXT_CLASS
nello stesso modo dei singoli membri da esportare.
Ad esempio:
class CExampleDialog : public CDialog
{
public:
AFX_EXT_CLASS CExampleDialog();
AFX_EXT_CLASS int DoModal();
// rest of class definition
// ...
};
Quando si esegue questa operazione, è possibile che si verifichi un problema aggiuntivo perché non si esportano tutti i membri della classe. Il problema è il funzionamento delle macro MFC. Diverse macro helper di MFC dichiarano o definiscono effettivamente i membri dati. Anche la DLL deve esportare questi membri dati.
Ad esempio, la macro DECLARE_DYNAMIC viene definita come segue quando si compila una DLL di estensione MFC:
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
La riga che inizia static AFX_DATA
dichiara un oggetto statico all'interno della classe. Per esportare correttamente questa classe e accedere alle informazioni di runtime da un file EXE client, è necessario esportare questo oggetto statico. Poiché l'oggetto statico viene dichiarato con il modificatore AFX_DATA
, è necessario definire AFX_DATA
come __declspec(dllexport)
quando si compila la DLL. Definirlo come __declspec(dllimport)
quando si compila il file eseguibile client.
Come illustrato in precedenza, AFX_EXT_CLASS
è già definito in questo modo. È sufficiente ridefinire AFX_DATA
in modo che corrisponda alla AFX_EXT_CLASS
definizione della classe.
Ad esempio:
#undef AFX_DATA
#define AFX_DATA AFX_EXT_CLASS
class CExampleView : public CView
{
DECLARE_DYNAMIC()
// ... class definition ...
};
#undef AFX_DATA
#define AFX_DATA
MFC usa sempre il AFX_DATA
simbolo sugli elementi di dati definiti all'interno delle relative macro, quindi questa tecnica funzionerà per tutti questi scenari. Ad esempio, funzionerà per DECLARE_MESSAGE_MAP.
Nota
Se si esporta l'intera classe anziché i membri selezionati della classe, i membri dati statici vengono esportati automaticamente.
È possibile utilizzare la stessa tecnica per esportare automaticamente l'operatore CArchive
di estrazione per le classi che usano le macro DECLARE_edizione Standard RIAL e IMPLEMENT_edizione Standard RIAL. Esportare l'operatore archive tra parentesi tra parentesi le dichiarazioni di classe (che si trovano nel file di intestazione) con il codice seguente:
#undef AFX_API
#define AFX_API AFX_EXT_CLASS
/* your class declarations here */
#undef AFX_API
#define AFX_API
Limitazioni di _AFXEXT
È possibile usare il _AFXEXT
simbolo del preprocessore per le DLL dell'estensione MFC, purché non si disponga di più livelli di DLL di estensione MFC. Se sono presenti DLL di estensione MFC che chiamano o derivano da classi nelle DLL dell'estensione MFC, che quindi derivano dalle classi MFC, è necessario usare il proprio simbolo del preprocessore per evitare ambiguità.
Il problema è che in Win32 è necessario dichiarare in modo esplicito qualsiasi dato come __declspec(dllexport)
esportarlo da una DLL e __declspec(dllimport)
importarlo da una DLL. Quando si definisce _AFXEXT
, le intestazioni MFC assicurano che AFX_EXT_CLASS
sia definito correttamente.
Quando si dispone di più livelli, un simbolo come AFX_EXT_CLASS
non è sufficiente: una DLL di estensione MFC può esportare le proprie classi e importare anche altre classi da un'altra DLL di estensione MFC. Per risolvere questo problema, usare un simbolo speciale del preprocessore che indica che si sta compilando la DLL stessa, anziché usare la DLL. Si supponga, ad esempio, che due DLL di estensione MFC, A.DLL
e B.DLL
. Ognuna esporta alcune classi rispettivamente in A.H
e B.H
. B.DLL
usa le classi di A.DLL
. I file di intestazione sono simili al seguente:
/* A.H */
#ifdef A_IMPL
#define CLASS_DECL_A __declspec(dllexport)
#else
#define CLASS_DECL_A __declspec(dllimport)
#endif
class CLASS_DECL_A CExampleA : public CObject
{ /* ... class definition ... */ };
/* B.H */
#ifdef B_IMPL
#define CLASS_DECL_B __declspec(dllexport)
#else
#define CLASS_DECL_B __declspec(dllimport)
#endif
class CLASS_DECL_B CExampleB : public CExampleA
{ /* ... class definition ... */ };
Quando A.DLL
viene compilato, viene compilato con /DA_IMPL
e B.DLL
quando viene compilato, viene compilato con /DB_IMPL
. Usando simboli separati per ogni DLL, CExampleB
viene esportato e CExampleA
importato durante la compilazione B.DLL
di . CExampleA
viene esportato durante la compilazione A.DLL
e l'importazione quando viene usato da o da B.DLL
un altro client.
Questo tipo di sovrapposizione non può essere eseguito quando si usano i AFX_EXT_CLASS
simboli predefiniti e _AFXEXT
del preprocessore. La tecnica descritta in precedenza risolve questo problema nello stesso modo in cui MFC esegue. MFC usa questa tecnica durante la compilazione delle DLL dell'estensione OLE, Database e Network MFC.
Ancora non esportazione dell'intera classe
Anche in questo caso, è necessario prestare particolare attenzione quando non si esporta un'intera classe. Assicurarsi che gli elementi di dati necessari creati dalle macro MFC vengano esportati correttamente. A tale scopo, è possibile ridefinire AFX_DATA
la macro della classe specifica. Ridefinirlo ogni volta che non si esporta l'intera classe.
Ad esempio:
// A.H
#ifdef A_IMPL
#define CLASS_DECL_A _declspec(dllexport)
#else
#define CLASS_DECL_A _declspec(dllimport)
#endif
#undef AFX_DATA
#define AFX_DATA CLASS_DECL_A
class CExampleA : public CObject
{
DECLARE_DYNAMIC()
CLASS_DECL_A int SomeFunction();
// class definition
// ...
};
#undef AFX_DATA
#define AFX_DATA
DllMain
Ecco il codice da inserire nel file di origine principale per la DLL dell'estensione MFC. Dovrebbe venire dopo lo standard include. Quando si usa AppWizard per creare file di avvio per una DLL di estensione MFC, viene fornito automaticamente un oggetto DllMain
.
#include "afxdllx.h"
static AFX_EXTENSION_MODULE extensionDLL;
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
// MFC extension DLL one-time initialization
if (!AfxInitExtensionModule(
extensionDLL, hInstance))
return 0;
// TODO: perform other initialization tasks here
}
else if (dwReason == DLL_PROCESS_DETACH)
{
// MFC extension DLL per-process termination
AfxTermExtensionModule(extensionDLL);
// TODO: perform other cleanup tasks here
}
return 1; // ok
}
La chiamata a AfxInitExtensionModule
acquisisce le classi di runtime del modulo (CRuntimeClass
strutture) e le relative object factory (COleObjectFactory
oggetti) da usare in un secondo momento quando viene creato l'oggetto CDynLinkLibrary
. La chiamata (facoltativa) a consente a AfxTermExtensionModule
MFC di pulire la DLL dell'estensione MFC quando ogni processo si disconnette (che si verifica quando il processo viene chiuso o quando la DLL viene scaricata da una FreeLibrary
chiamata) dalla DLL dell'estensione MFC. Poiché la maggior parte delle DLL dell'estensione MFC non viene caricata in modo dinamico (in genere, sono collegate tramite le librerie di importazione), la chiamata a AfxTermExtensionModule
in genere non è necessaria.
Se l'applicazione carica e libera le DLL dell'estensione MFC in modo dinamico, assicurarsi di chiamare AfxTermExtensionModule
come illustrato in precedenza. Assicurarsi anche di usare AfxLoadLibrary
e AfxFreeLibrary
(invece delle funzioni LoadLibrary
Win32 e FreeLibrary
) se l'applicazione usa più thread o se carica dinamicamente una DLL di estensione MFC. L'uso AfxLoadLibrary
e AfxFreeLibrary
garantisce che il codice di avvio e arresto eseguito quando la DLL dell'estensione MFC viene caricata e scaricata non danneggia lo stato MFC globale.
Il file AFXDLLX.H
di intestazione contiene definizioni speciali per le strutture usate nelle DLL dell'estensione MFC, ad esempio la definizione per AFX_EXTENSION_MODULE
e CDynLinkLibrary
.
L'estensione globaleDLL deve essere dichiarata come illustrato. A differenza della versione a 16 bit di MFC, è possibile allocare memoria e chiamare funzioni MFC durante questo periodo, poiché l'oggetto MFCxx.DLL
viene inizializzato completamente dal momento DllMain
in cui viene chiamato .
Condivisione di risorse e classi
Le DLL di estensione MFC semplici richiedono solo poche funzioni a larghezza di banda ridotta nell'applicazione client e niente di più. Le DLL a elevato utilizzo dell'interfaccia utente possono voler esportare risorse e classi C++ nell'applicazione client.
L'esportazione delle risorse viene eseguita tramite un elenco di risorse. In ogni applicazione è un elenco CDynLinkLibrary
di oggetti collegato singolarmente. Quando si cerca una risorsa, la maggior parte delle implementazioni MFC standard che caricano le risorse esamina prima il modulo di risorse corrente (AfxGetResourceHandle
) e, se non viene trovato, viene visualizzato l'elenco di oggetti che tentano di CDynLinkLibrary
caricare la risorsa richiesta.
La creazione dinamica di oggetti C++ in base al nome di una classe C++ è simile. Il meccanismo di deserializzazione dell'oggetto MFC deve avere tutti gli CRuntimeClass
oggetti registrati in modo che possa ricostruire creando dinamicamente l'oggetto C++ del tipo richiesto in base a quanto archiviato in precedenza.
Se si vuole che l'applicazione client usi classi nella DLL dell'estensione MFC , DECLARE_SERIAL
sarà necessario esportare le classi per renderle visibili all'applicazione client. È anche fatto camminando la CDynLinkLibrary
lista.
Nell'esempio DLLHUSK
MFC Advanced Concepts l'elenco è simile al seguente:
head -> DLLHUSK.EXE - or - DLLHUSK.EXE
| |
TESTDLL2.DLL TESTDLL2.DLL
| |
TESTDLL1.DLL TESTDLL1.DLL
| |
| |
MFC90D.DLL MFC90.DLL
La MFCxx.DLL
voce in genere è l'ultima nell'elenco di risorse e classi. MFCxx.DLL
include tutte le risorse MFC standard, incluse le stringhe del prompt per tutti gli ID dei comandi standard. Posizionarlo alla fine dell'elenco consente sia alle DLL che all'applicazione client stessa di basarsi sulle risorse condivise in MFCxx.DLL
, invece di avere le proprie copie.
L'unione delle risorse e dei nomi di classe di tutte le DLL nello spazio dei nomi dell'applicazione client presenta lo svantaggio che è necessario prestare attenzione agli ID o ai nomi scelti. È possibile disabilitare questa funzionalità non esportando le risorse o un CDynLinkLibrary
oggetto nell'applicazione client. L'esempio DLLHUSK
gestisce lo spazio dei nomi delle risorse condivise usando più file di intestazione. Per altri suggerimenti sull'uso dei file di risorse condivise, vedere La nota tecnica 35 .
Inizializzazione della DLL
Come accennato in precedenza, in genere si vuole creare un CDynLinkLibrary
oggetto per esportare le risorse e le classi nell'applicazione client. È necessario fornire un punto di ingresso esportato per inizializzare la DLL. Minimamente, è una void
routine che non accetta argomenti e non restituisce nulla, ma può essere qualsiasi cosa che ti piace.
Ogni applicazione client che vuole usare la DLL deve chiamare questa routine di inizializzazione, se si usa questo approccio. È anche possibile allocare questo CDynLinkLibrary
oggetto nell'oggetto DllMain
subito dopo aver chiamato AfxInitExtensionModule
.
La routine di inizializzazione deve creare un CDynLinkLibrary
oggetto nell'heap dell'applicazione corrente, collegato alle informazioni della DLL dell'estensione MFC. A tale scopo, è possibile definire una funzione simile alla seguente:
extern "C" extern void WINAPI InitXxxDLL()
{
new CDynLinkLibrary(extensionDLL);
}
Il nome della routine, InitXxxDLL in questo esempio, può essere qualsiasi elemento desiderato. Non è necessario che extern "C"
sia , ma rende l'elenco di esportazione più semplice da gestire.
Nota
Se si usa la DLL dell'estensione MFC da una NORMALE DLL MFC, è necessario esportare questa funzione di inizializzazione. Questa funzione deve essere chiamata dalla normale DLL MFC prima di usare qualsiasi classe o risorsa DLL di estensione MFC.
Esportazione di voci
Il modo semplice per esportare le classi consiste nell'usare __declspec(dllimport)
e __declspec(dllexport)
in ogni classe e funzione globale da esportare. È molto più semplice, ma è meno efficiente rispetto alla denominazione di ogni punto di ingresso in un file DEF, come descritto di seguito. Ciò è dovuto al fatto che si ha meno controllo sulle funzioni esportate. Inoltre, non è possibile esportare le funzioni in base all'ordinale. TESTDLL1 e TESTDLL2 utilizzare questo metodo per esportare le voci.
Un metodo più efficiente consiste nell'esportare ogni voce assegnandogli un nome nel file DEF. Questo metodo viene usato da MFCxx.DLL
. Poiché l'esportazione viene eseguita in modo selettivo dalla DLL, è necessario decidere quali interfacce specifiche si desidera esportare. È difficile, poiché è necessario specificare i nomi mangled al linker sotto forma di voci nel file DEF. Non esportare alcuna classe C++ a meno che non sia effettivamente necessario avere un collegamento simbolico.
Se si è tentato di esportare le classi C++ con un file DEF in precedenza, è possibile sviluppare uno strumento per generare automaticamente questo elenco. Questa operazione può essere eseguita usando un processo di collegamento a due fasi. Collegare la DLL una sola volta senza esportazioni e consentire al linker di generare un file MAP. Il file MAP contiene un elenco di funzioni da esportare. Con alcune operazioni di riorganizzazione, è possibile usarla per generare le voci EXPORT per il file DEF. L'elenco di esportazione per MFCxx.DLL
e le DLL dell'estensione OLE e MFC del database, diverse migliaia di numeri, è stato generato con un processo di questo tipo (anche se non è completamente automatico e richiede un'ottimizzazione manuale ogni volta in un po').
CWinApp e CDynLinkLibrary
Una DLL dell'estensione MFC non dispone di un CWinApp
oggetto derivato da - proprio. Deve invece funzionare con l'oggetto CWinApp
derivato da -dell'applicazione client. Significa che l'applicazione client è proprietaria del pump del messaggio principale, del ciclo di inattività e così via.
Se la DLL dell'estensione MFC deve gestire dati aggiuntivi per ogni applicazione, è possibile derivare una nuova classe da CDynLinkLibrary
e crearla nella routine descritta in InitXxxDLL
precedenza. Durante l'esecuzione, la DLL può controllare l'elenco di CDynLinkLibrary
oggetti dell'applicazione corrente per trovare quello per la DLL dell'estensione MFC specifica.
Uso delle risorse nell'implementazione della DLL
Come accennato in precedenza, il caricamento predefinito delle risorse illustra l'elenco di CDynLinkLibrary
oggetti che cercano il primo file EXE o DLL con la risorsa richiesta. Tutte le API MFC e tutto il codice interno usano AfxFindResourceHandle
per esaminare l'elenco delle risorse per trovare qualsiasi risorsa, indipendentemente dalla posizione in cui si trova.
Se si desidera caricare solo le risorse da una posizione specifica, usare le API AfxGetResourceHandle
e AfxSetResourceHandle
salvare l'handle precedente e impostare il nuovo handle. Assicurarsi di ripristinare l'handle di risorse precedente prima di tornare all'applicazione client. L'esempio TESTDLL2 usa questo approccio per caricare in modo esplicito un menu.
L'elenco a piedi presenta alcuni svantaggi: è leggermente più lento e richiede la gestione degli intervalli di ID risorsa. Ha il vantaggio che un'applicazione client che collega diverse DLL di estensione MFC può usare qualsiasi risorsa fornita da DLL senza dover specificare l'handle dell'istanza dll. AfxFindResourceHandle
è un'API usata per l'spostamento dell'elenco di risorse per cercare una determinata corrispondenza. Accetta il nome e il tipo di una risorsa e restituisce l'handle di risorsa in cui trova prima la risorsa o NULL.
Scrittura di un'applicazione che usa la versione della DLL
Requisiti dell'applicazione
Un'applicazione che usa la versione condivisa di MFC deve seguire alcune regole di base:
Deve avere un
CWinApp
oggetto e seguire le regole standard per una pompa di messaggi.Deve essere compilato con un set di flag del compilatore obbligatori (vedere di seguito).
Deve essere collegato alle librerie di importazione MFCxx. Impostando i flag del compilatore necessari, le intestazioni MFC determinano in fase di collegamento la libreria a cui deve collegarsi l'applicazione.
Per eseguire il file eseguibile,
MFCxx.DLL
deve trovarsi nel percorso o nella directory di sistema di Windows.
Compilazione con l'ambiente di sviluppo
Se si usa il makefile interno con la maggior parte delle impostazioni predefinite standard, è possibile modificare facilmente il progetto per compilare la versione della DLL.
Il passaggio seguente presuppone che sia presente un'applicazione MFC funzionante collegata con NAFXCWD.LIB
(per il debug) e (per la versione) e NAFXCW.LIB
si vuole convertirla in modo da usare la versione condivisa della libreria MFC. Si esegue l'ambiente di Visual Studio e si dispone di un file di progetto interno.
- Scegliere Proprietà dal menu Progetti. Nella pagina Generale in Impostazioni predefinite progetto impostare Classi Microsoft Foundation su Usa MFC in una DLL condivisa (MFCxx(d).dll).
Compilazione con NMAKE
Se si usa la funzionalità makefile esterna del compilatore o si usa direttamente NMAKE, è necessario modificare il makefile per supportare le opzioni del compilatore e del linker necessarie.
Flag del compilatore obbligatori:
/D_AFXDLL /MD
/D_AFXDLL
Le intestazioni MFC standard devono essere definite dal _AFXDLL
simbolo.
/MD
L'applicazione deve usare la versione DLL della libreria di runtime C.
Tutti gli altri flag del compilatore seguono le impostazioni predefinite MFC , _DEBUG
ad esempio per il debug.
Modificare l'elenco dei linker delle librerie. Modificare NAFXCWD.LIB
in MFCxxD.LIB
e NAFXCW.LIB
in MFCxx.LIB
. Sostituisci LIBC.LIB
con MSVCRT.LIB
. Come per qualsiasi altra libreria MFC, è importante posizionarla MFCxxD.LIB
prima di qualsiasi libreria C-runtime.
Facoltativamente, aggiungere /D_AFXDLL
sia alle opzioni del compilatore di risorse di rilascio che a quello di debug (quello che compila effettivamente le risorse con /R
). Questa opzione rende il file eseguibile finale più piccolo condividendo le risorse presenti nelle DLL MFC.
Dopo aver apportato queste modifiche, è necessaria una ricompilazione completa.
Compilazione degli esempi
La maggior parte dei programmi di esempio MFC può essere compilata da Visual C++ o da un MAKEFILE compatibile con NMAKE condiviso dalla riga di comando.
Per convertire uno di questi esempi in modo da usare MFCxx.DLL
, è possibile caricare il file MAK in Visual C++ e impostare le opzioni di Project come descritto in precedenza. Se si usa la compilazione NMAKE, è possibile specificare AFXDLL=1
nella riga di comando di NMAKE e compilare l'esempio usando le librerie MFC condivise.
L'esempio MFC Advanced Concepts DLLHUSK viene compilato con la versione DLL di MFC. Questo esempio non solo illustra come compilare un'applicazione collegata a MFCxx.DLL
, ma illustra anche altre funzionalità dell'opzione di creazione di pacchetti DLL MFC, ad esempio DLL di estensione MFC descritte più avanti in questa nota tecnica.
Note sulla creazione di pacchetti
Le versioni di rilascio delle DLL (MFCxx.DLL
e MFCxxU.DLL
) sono ridistribuibili liberamente. Le versioni di debug delle DLL non sono ridistribuibili liberamente e devono essere usate solo durante lo sviluppo dell'applicazione.
Le DLL di debug vengono fornite con informazioni di debug. Usando il debugger di Visual C++, è possibile tracciare l'esecuzione dell'applicazione e della DLL. Le DLL di versione (MFCxx.DLL
e MFCxxU.DLL
) non contengono informazioni di debug.
Se si personalizzano o ricompilano le DLL, è necessario chiamarle diverso da "MFCxx". Il file MFCDLL.MAK
SRC MFC descrive le opzioni di compilazione e contiene la logica per rinominare la DLL. La ridenominazione dei file è necessaria, poiché queste DLL sono potenzialmente condivise da molte applicazioni MFC. Se la versione personalizzata delle DLL MFC sostituisce quelle installate nel sistema, potrebbe interrompere un'altra applicazione MFC usando le DLL MFC condivise.
La ricompilazione delle DLL MFC non è consigliata.
Come viene implementato MFCxx.DLL
La sezione seguente descrive come viene implementata la DLL MFC (MFCxx.DLL
e MFCxxD.DLL
). Comprendere i dettagli qui non è importante anche se si vuole usare la DLL MFC con l'applicazione. I dettagli qui non sono essenziali per comprendere come scrivere una DLL di estensione MFC, ma la comprensione di questa implementazione può aiutare a scrivere una DLL personalizzata.
Panoramica dell'implementazione
La DLL MFC è davvero un caso speciale di una DLL di estensione MFC come descritto in precedenza. Ha un numero elevato di esportazioni per un numero elevato di classi. Nella DLL MFC sono disponibili alcune operazioni aggiuntive che lo rendono ancora più speciale rispetto a una normale DLL di estensione MFC.
Win32 esegue la maggior parte del lavoro
La versione a 16 bit di MFC richiedeva una serie di tecniche speciali, tra cui i dati per app nel segmento dello stack, segmenti speciali creati da codice assembly 80x86, contesti di eccezione per processo e altre tecniche. Win32 supporta direttamente i dati per processo in una DLL, che è ciò che vuoi per la maggior parte del tempo. Per la maggior parte dei casi MFCxx.DLL
è appena NAFXCW.LIB
incluso in un pacchetto in una DLL. Se si esamina il codice sorgente MFC, si troveranno alcuni #ifdef _AFXDLL
casi, poiché non ci sono molti casi speciali che devono essere fatti. I casi speciali che sono presenti in particolare per gestire Win32 in Windows 3.1 (altrimenti noto come Win32s). Win32s non supporta direttamente i dati DLL per processo. La DLL MFC deve usare le API Win32 di archiviazione locale thread per ottenere i dati locali.
Impatto sulle origini della libreria, file aggiuntivi
L'impatto della _AFXDLL
versione sulle normali origini e intestazioni della libreria di classi MFC è relativamente minore. Esiste un file di versione speciale (AFXV_DLL.H
) e un file di intestazione aggiuntivo (AFXDLL_.H
) incluso dall'intestazione principale AFXWIN.H
. L'intestazione AFXDLL_.H
include la CDynLinkLibrary
classe e altri dettagli di implementazione delle _AFXDLL
applicazioni e delle DLL dell'estensione MFC. L'intestazione AFXDLLX.H
viene fornita per la compilazione di DLL di estensione MFC (vedere sopra per informazioni dettagliate).
Le origini regolari della libreria MFC in MFC SRC hanno codice condizionale aggiuntivo nel _AFXDLL
#ifdef. Un file di origine aggiuntivo (DLLINIT.CPP
) contiene il codice di inizializzazione della DLL aggiuntivo e un'altra associazione per la versione condivisa di MFC.
Per compilare la versione condivisa di MFC, vengono forniti file aggiuntivi. Per informazioni dettagliate su come compilare la DLL, vedere di seguito.
Due file DEF vengono usati per esportare i punti di ingresso della DLL MFC per le versioni di debug (
MFCxxD.DEF
) e versione (MFCxx.DEF
) della DLL.Un file RC (
MFCDLL.RC
) contiene tutte le risorse MFC standard e unaVERSIONINFO
risorsa per la DLL.Viene fornito un file CLW (
MFCDLL.CLW
) per consentire l'esplorazione delle classi MFC tramite ClassWizard. Questa funzionalità non è particolare per la versione DLL di MFC.
Gestione della memoria
Un'applicazione che usa MFCxx.DLL
un allocatore di memoria comune fornito da MSVCRTxx.DLL
, la DLL C-runtime condivisa. L'applicazione, tutte le DLL di estensione MFC e le DLL MFC usano questo allocatore di memoria condivisa. Usando una DLL condivisa per l'allocazione di memoria, le DLL MFC possono allocare memoria liberata successivamente dall'applicazione o viceversa. Poiché sia l'applicazione che la DLL devono usare lo stesso allocatore, non è consigliabile eseguire l'override di C++ globale operator new
o operator delete
. Le stesse regole si applicano al resto delle routine di allocazione della memoria in fase di esecuzione C , ad esempio malloc
, realloc
free
, e altre.
Ordinali e classi __declspec(dllexport) e denominazione dll
Non viene usata la class
__declspec(dllexport)
funzionalità del compilatore C++. Viene invece incluso un elenco di esportazioni con le origini della libreria di classi (MFCxx.DEF
e MFCxxD.DEF
). Vengono esportati solo un set selezionato di punti di ingresso (funzioni e dati). Altri simboli, ad esempio le funzioni o le classi di implementazione privata MFC, non vengono esportati. Tutte le esportazioni vengono eseguite dall'ordinale senza un nome stringa nella tabella dei nomi residente o non residente.
L'uso class
__declspec(dllexport)
di può essere un'alternativa valida per la creazione di DLL più piccole, ma in una DLL di grandi dimensioni come MFC, il meccanismo di esportazione predefinito presenta limiti di efficienza e capacità.
Ciò che significa tutto è che è possibile creare un pacchetto di una grande quantità di funzionalità nella versione MFCxx.DLL
che è solo di circa 800 KB senza compromettere molta velocità di esecuzione o caricamento. MFCxx.DLL
sarebbe stato 100 KB più grande se questa tecnica non fosse stata usata. La tecnica consente di aggiungere punti di ingresso aggiuntivi alla fine del file DEF. Consente un controllo delle versioni semplice senza compromettere la velocità e l'efficienza delle dimensioni dell'esportazione tramite ordinale. Le revisioni delle versioni principali nella libreria di classi MFC modificheranno il nome della libreria. Ovvero, MFC30.DLL
è la DLL ridistribuibile contenente la versione 3.0 della libreria di classi MFC. Un aggiornamento di questa DLL, ad esempio, in un ipotetico MFC 3.1, la DLL verrà invece denominata MFC31.DLL
. Anche in questo caso, se si modifica il codice sorgente MFC per produrre una versione personalizzata della DLL MFC, usare un nome diverso (e preferibilmente uno senza "MFC" nel nome).