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 entre si, semelhantes às funções mutuamente recursivas.

O problema com a importação mútua de arquivos executáveis (geralmente DLLs) é que nenhum deles pode ser criado sem criar 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 compilar todas as bibliotecas de importação necessárias, independentemente de quantas DLLs estão envolvidas ou quão complicadas são as dependências.

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

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

    Executar o LIB com a opção /DEF produz um arquivo adicional com uma extensão .EXP. O arquivo .EXP deve ser usado posteriormente para criar o arquivo executável.

  2. Depois de usar o LINK ou o LIB para criar todas as bibliotecas de importação, volte e execute o LINK para criar 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, o LIB também teria produzido o arquivo DLL1.exp. 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. A etapa 1 é executar o 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 símbolos de DLL2. A etapa 3 cria a DLL1 usando DLL1.exp e DLL2.lib como entrada. Observe que um arquivo .exp para DLL2 não é necessário porque o LIB não foi usado para criar a biblioteca de importação da 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 de _AFXEXT

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

O problema é que, no Win32, você deve declarar explicitamente os dados como __declspec(dllexport) se fossem exportados de uma DLL e __declspec(dllimport) se eles devem ser importados de uma DLL. Quando você define _AFXEXT, os cabeçalhos MFC garantem que AFX_EXT_CLASS esteja definido corretamente.

Quando você tem várias camadas, um símbolo como AFX_EXT_CLASS não é suficiente, pois 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 esse problema, use um símbolo especial de pré-processador que indica que você está criando a própria DLL versus usando a DLL. Por exemplo, imagine duas DLLs de extensão MFC, A.dll e B.dll. Cada uma delas exporta algumas classes em A.h e B.h, respectivamente. B.dll usa as classes de A.dll. Os arquivos de cabeçalho devem ser parecidos com este:

/* 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 criar 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 de pré-processador internos AFX_EXT_CLASS e _AFXEXT. A técnica descrita acima resolve esse problema de uma maneira diferente do mecanismo que o próprio MFC usa ao criar suas DLLs de extensão de MFC de rede, de Banco de dados e Tecnologias ativas.

Sem exportação da classe inteira

Quando você não estiver exportando uma classe inteira, precisará 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 classe específica. Deverá ser feito sempre que não exportar 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 você deseja fazer?

Que mais você deseja saber?

Confira também

Importando e exportando