Partilhar via


Importações Mútuas

Exportar ou importar para outro arquivo executável apresenta complicações quando as importações são mútuas (ou circulares). Por exemplo, duas DLLs importam símbolos uma da outra, semelhantes a funções mutuamente recursivas.

O problema com a importação mútua de arquivos executáveis (geralmente DLLs) é que nenhum deles pode ser construído sem construir o outro primeiro. Cada processo de compilação requer, como entrada, uma biblioteca de importação produzida pelo outro processo de compilação.

A solução é usar o utilitário LIB com a opção /DEF, que produz uma biblioteca de importação sem criar o arquivo executável. Usando esse utilitário, você pode criar todas as bibliotecas de importação necessárias, não importa quantas DLLs estejam envolvidas ou quão complicadas sejam as dependências.

A solução geral para lidar com as importações mútuas é:

  1. Tome cada DLL uma de cada vez. (Qualquer pedido é viável, embora alguns pedidos sejam mais ideais.) Se todas as bibliotecas de importação necessárias existirem e estiverem atualizadas, execute LINK para criar o arquivo executável (DLL). Isso produz uma biblioteca de importação. Caso contrário, execute LIB para produzir uma biblioteca de importação.

    A execução do LIB com a opção /DEF produz um ficheiro adicional com a extensão .EXP. O ficheiro .EXP deve ser usado mais tarde para criar o ficheiro executável.

  2. Depois de usar LINK ou LIB para criar todas as bibliotecas de importação, volte e execute LINK para criar quaisquer arquivos executáveis que não foram criados na etapa anterior. Observe que o arquivo .exp correspondente deve ser especificado na linha LINK.

    Se você tivesse executado o utilitário LIB anteriormente para produzir uma biblioteca de importação para DLL1, LIB teria produzido o arquivo DLL1.exp também. Você deve usar DLL1.exp como entrada para LINK ao criar DLL1.dlll.

A ilustração a seguir mostra uma solução para duas DLLs de importação mútua, DLL1 e DLL2. O passo 1 é executar LIB, com a opção /DEF definida, em DLL1. A etapa 1 produz DLL1.lib, uma biblioteca de importação, e DLL1.exp. Na etapa 2, a biblioteca de importação é usada para criar a DLL2, que, por sua vez, produz uma biblioteca de importação para os símbolos da DLL2. A etapa 3 cria DLL1, usando DLL1.exp e DLL2.lib como entrada. Observe que um arquivo .exp para DLL2 não é necessário porque LIB não foi usado para criar a biblioteca de importação do DLL2.

Diagrama que mostra as entradas e saídas quando você usa importações mútuas para vincular duas DLLs.
Vinculando duas DLLs com importações mútuas

Limitações do _AFXEXT

Você pode usar o _AFXEXT símbolo de pré-processador para suas DLLs de extensão MFC, desde que não tenha várias camadas de DLLs de extensão MFC. Se tiver DLLs de extensão MFC que chamam ou derivam de classes nas suas próprias DLLs de extensão MFC, as quais por sua vez derivam das classes MFC, deve usar o seu próprio símbolo do pré-processador para evitar ambiguidade.

O problema é que no Win32, você deve declarar explicitamente quaisquer dados como __declspec(dllexport) se ele deve ser exportado de uma DLL, e __declspec(dllimport) se ele deve ser importado de uma DLL. Quando define _AFXEXT, os cabeçalhos MFC asseguram que AFX_EXT_CLASS está definido corretamente.

Quando você tem várias camadas, um símbolo como AFX_EXT_CLASS não é suficiente, porque uma DLL de extensão MFC pode estar exportando novas classes, bem como importando outras classes de outra DLL de extensão MFC. Para resolver este problema, use um símbolo especial de pré-processador que indique que está a criar a DLL em si versus a usar a DLL. Por exemplo, imagine duas DLLs de extensão MFC, A.dll e B.dll. Cada um deles exporta algumas classes em A.h ou B.h, respetivamente. B.dll usa as classes de A.dll. Os arquivos de cabeçalho teriam a seguinte aparência:

/* 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 é construído, ele é construído com /D A_IMPL e quando B.dll é construído, ele é construído com /D B_IMPL. Usando símbolos separados para cada DLL, CExampleB é exportado e CExampleA é importado ao criar B.dll. CExampleA é exportado ao construir A.dll e importado quando usado por B.dll (ou algum outro cliente).

Esse tipo de camada não pode ser feito ao usar os símbolos AFX_EXT_CLASS e _AFXEXT pré-processador integrados. A técnica descrita acima resolve esse problema de uma maneira não muito diferente do mecanismo que o próprio MFC usa ao criar suas tecnologias ativas, banco de dados e DLLs de extensão MFC de rede.

Não exportar a classe inteira

Quando você não estiver exportando uma classe inteira, você precisa garantir que os itens de dados necessários criados pelas macros MFC sejam exportados corretamente. Isso pode ser feito redefinindo AFX_DATA para a macro da sua classe específica. Isso deve ser feito sempre que você não estiver exportando a classe inteira.

Por exemplo:

/* 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

O que pretende fazer?

Sobre o que quer saber mais?

Ver também

Importação e exportação de