DLLs de extensão de MFC
Uma DLL de extensão da MFC é uma DLL que normalmente implementa classes reutilizáveis derivadas de classes existentes da biblioteca Microsoft Foundation Class.
Uma DLL de extensão da MFC tem os seguintes recursos e requisitos:
O executável do cliente deve ser um aplicativo MFC compilado com
_AFXDLL
definido.Uma DLL de extensão da MFC também pode ser usada por uma DLL da MFC regular que está vinculada dinamicamente à MFC.
As DLLs de extensão MFC devem ser compiladas com
_AFXEXT
definido. Isso força a definição de_AFXDLL
e garante que as declarações adequadas sejam extraídas dos arquivos de cabeçalho da MFC. Ele também garante queAFX_EXT_CLASS
seja definido como__declspec(dllexport)
durante a criação da DLL, o que é necessário se você estiver usando essa macro para declarar as classes em sua DLL de extensão MFC.As DLLs de extensão da MFC não devem criar uma instância de uma classe derivada de
CWinApp
, mas devem depender do aplicativo cliente (ou DLL) para fornecer esse objeto.No entanto, as DLLs de extensão da MFC devem fornecer uma função
DllMain
e fazer qualquer inicialização necessária ali.
As DLLs de extensão são criadas usando a versão de biblioteca de link dinâmico da MFC (também conhecida como a versão compartilhada da MFC). Somente executáveis do MFC (aplicativos ou DLLs regulares do MFC) criados com a versão compartilhada do MFC podem usar uma DLL de extensão do MFC. O aplicativo cliente e a DLL de extensão da MFC devem usar a mesma versão do MFCx0.dll. Com uma DLL de extensão da MFC, você pode derivar novas classes personalizadas da MFC e, em seguida, oferecer essa versão estendida da MFC para aplicativos que chamam sua DLL.
DLLs de extensão também podem ser usadas para passar objetos derivados da MFC entre o aplicativo e a DLL. As funções de membro associadas ao objeto passado existem no módulo em que o objeto foi criado. Como essas funções são exportadas corretamente ao usar a versão de DLL compartilhada do MFC, você pode passar livremente ponteiros de objeto derivados do MFC ou a MFC entre um aplicativo e as DLLs de extensão do MFC que ela carrega.
Uma DLL de extensão da MFC usa uma versão compartilhada da MFC da mesma forma que um aplicativo usa a versão de DLL compartilhada da MFC, com algumas considerações adicionais:
Ela não tem um objeto derivado de
CWinApp
. Ele deve funcionar com oCWinApp
objeto derivado do aplicativo cliente. Isso significa que o aplicativo cliente possui a bomba de mensagem principal, o loop ocioso, etc.Ele chama
AfxInitExtensionModule
em sua funçãoDllMain
. O valor retornado dessa função deve ser verificado. Se um valor zero for retornado dAfxInitExtensionModule
, retorne 0 da sua funçãoDllMain
.Ele criará um objeto CDynLinkLibrary durante a inicialização se a DLL da extensão MFC quiser exportar objetos ou recursos
CRuntimeClass
para o aplicativo.
Antes da versão 4.0 da MFC, esse tipo de DLL era chamado de AFXDLL. AFXDLL refere-se ao símbolo de pré-processador _AFXDLL
definido ao criar a DLL.
As bibliotecas de importação para a versão compartilhada da MFC são nomeadas de acordo com a convenção descrita nas Convenções de nomenclatura para DLLs da MFC. O Visual Studio fornece versões predefinidas das DLLs da MFC, além de várias DLLs não da MFC que você pode usar e distribuir com seus aplicativos. Elas estão documentadas no Redist.txt, que se encontra instalado na pasta Arquivos de Programas\Microsoft Visual Studio.
Se você estiver exportando usando um arquivo .def, coloque o seguinte código no início e no final do arquivo de cabeçalho:
#undef AFX_DATA
#define AFX_DATA AFX_EXT_DATA
// <body of your header file>
#undef AFX_DATA
#define AFX_DATA
Essas quatro linhas garantem que o código seja compilado corretamente para uma DLL de extensão da MFC. Deixar de fora essas quatro linhas pode fazer com que sua DLL seja compilada ou vinculada incorretamente.
Se você precisar passar um ponteiro de objeto MFC ou derivado de MFC para ou de uma DLL de MFC, a DLL deverá ser uma DLL de extensão da MFC. As funções de membro associadas ao objeto passado existem no módulo em que o objeto foi criado. Como essas funções são exportadas corretamente ao usar a versão de DLL compartilhada do MFC, você pode passar livremente ponteiros de objeto derivados do MFC ou a MFC entre um aplicativo e as DLLs de extensão do MFC que ela carrega.
Devido a problemas de mangling e exportação de nomes C++, a lista de exportação de uma DLL de extensão MFC pode ser diferente entre as versões de depuração e varejo das mesmas DLL e DLLs para diferentes plataformas. O MFCx0.dll de varejo tem cerca de 2.000 pontos de entrada exportados; o MFCx0D.dll de depuração tem cerca de 3.000 pontos de entrada exportados.
Gerenciamento de memória
MFCx0.dll e todas as DLLs de extensão MFC carregadas no espaço de endereço de um aplicativo cliente usam o mesmo alocador de memória, o carregamento de recursos e outros estados globais do MFC como se estivessem no mesmo aplicativo. Isso é significativo porque as bibliotecas DLL não MFC e as DLLs MFC regulares fazem exatamente o oposto e cada tem uma DLL alocando para fora do próprio pool de memória.
Se uma DLL de extensão MFC alocar memória, essa memória poderá se intercalar livremente com qualquer outro objeto alocado pelo aplicativo. Além disso, se um aplicativo que é vinculado dinamicamente ao MFC falhar, a proteção do sistema operacional manterá a integridade de qualquer outro aplicativo MFC que compartilha a DLL.
Da mesma forma, outros estados de MFC globais, como o arquivo executável atual do qual carregar recursos, também são compartilhados entre o aplicativo cliente e todas as DLLs de extensão da MFC, bem como o MFCx0.dll em si.
Compartilhando recursos e classes
A exportação de recursos é feita por meio de uma lista de recursos. Cada aplicativo contém uma lista vinculada de objetos CDynLinkLibrary . Ao procurar um recurso, a maioria das implementações padrão da MFC que carregam recursos olham primeiro para o módulo de recurso atual (AfxGetResourceHandle
) e, se o recurso não for encontrado, navegue na lista de objetos CDynLinkLibrary tentando carregar o recurso solicitado.
A navegação pela lista tem algumas desvantagens: é um pouco mais lenta e requer o gerenciamento de intervalos de ID de recursos. Ele tem a vantagem de que um aplicativo cliente que é vinculado a várias DLLs de extensão do MFC pode usar qualquer recurso fornecido por DLL sem precisar especificar o identificador da instância de DLL. AfxFindResourceHandle
é uma API usada para percorrer a lista de recursos para procurar uma determinada correspondência. O nome e o tipo de um recurso são usados e retornam o identificador de recurso onde o recurso é encontrado pela primeira vez ou NULL.
Se você não quiser percorrer a lista e carregar apenas recursos de um local específico, use as funções AfxGetResourceHandle
e AfxSetResourceHandle
para salvar o identificador antigo e defina o novo identificador. Confirme se restaura o identificador de recurso antigo antes de retornar ao aplicativo cliente. Para obter um exemplo de como usar essa abordagem para carregar explicitamente um menu, confira Testdll2 .cpp no MFC de amostra DLLHUSK.
A criação dinâmica de objetos da MFC dado um nome MFC é semelhante. O mecanismo de desserialização do objeto da MFC precisa ter todos os objetos CRuntimeClass
registrados para que ele possa ser reconstruído através da criação dinâmica de objetos C++ do tipo necessário com base no que foi armazenado anteriormente.
No caso da amosta de MFC DLLHUSK, a lista é semelhante a:
head -> DLLHUSK.EXE - or - DLLHUSK.EXE
| |
TESTDLL2.DLL TESTDLL2.DLL
| |
TESTDLL1.DLL TESTDLL1.DLL
| |
MFCOxxD.DLL |
| |
MFCDxxD.DLL |
| |
MFCxxD.DLL MFCxx.DLL
onde xx é o número da versão; por exemplo, 42 representa a versão 4.2.
O MFCxx.dll geralmente é o último na lista de recursos e classes. O MFCxx.dll inclui todos os recursos MFC padrão, incluindo cadeias de caracteres de prompt para todas as IDs de comando padrão. Colocá-lo no final da lista permite que as DLLs e o próprio aplicativo cliente não tenham a própria cópia dos recursos padrão da MFC, mas confiem nos recursos compartilhados no MFCxx.dll.
Mesclar os recursos e os nomes de classe de todas as DLLs no espaço de nome do aplicativo cliente tem a desvantagem de exigir que você tenha cuidado com quais IDs ou nomes escolher.
O exemplo DLLHUSK gerencia o espaço de nome de recurso compartilhado ao usar vários arquivos de cabeçalho.
Se sua DLL de extensão MFC precisar manter dados extras para cada aplicativo, você poderá derivar uma nova classe de CDynLinkLibrary e criá-la em DllMain
. Ao ser executado, a DLL pode verificar a lista de objetos CDynLinkLibrary do aplicativo atual para localizar o ideal para a DLL de extensão MFC específica.