Partilhar via


TN033: Versão DLL do MFC

Esta nota descreve como você pode usar o MFCxx.DLL e MFCxxD.DLL (onde xx é o número de versão do MFC) bibliotecas de vínculo dinâmico compartilhadas com aplicativos MFC e DLLs de extensão MFC. Para obter mais informações sobre DLLs MFC regulares, consulte Usando MFC como parte de uma DLL.

Esta nota técnica abrange três aspetos das DLLs. Os dois últimos são para os usuários mais avançados:

Se você estiver interessado em criar uma DLL usando MFC que pode ser usada com aplicativos não-MFC (conhecido como uma DLL, MFC regular), consulte a Nota Técnica 11.

Visão geral do suporte MFCxx.DLL: terminologia e arquivos

DLL MFC regular: Você usa uma DLL MFC regular para criar uma DLL autônoma usando algumas das classes MFC. As interfaces através do limite App/DLL são interfaces "C" e o aplicativo cliente não precisa ser um aplicativo MFC.

DLLs MFC regulares são a versão das DLLs suportadas no MFC 1.0. Eles são descritos na Nota Técnica 11 e no exemplo DLLScreenCapMFC Advanced Concepts .

Observação

A partir do Visual C++ versão 4.0, o termo USRDLL é obsoleto e foi substituído por uma DLL MFC regular que estaticamente se vincula ao MFC. Você também pode criar uma DLL MFC regular que dinamicamente se vincula ao MFC.

MFC 3.0 (e superior) suporta DLLs MFC regulares com todas as novas funcionalidades, incluindo as classes OLE e Database.

AFXDLL: Também conhecida como a versão compartilhada das bibliotecas MFC. É o novo suporte de DLL adicionado no MFC 2.0. A biblioteca MFC em si está em várias DLLs (descritas abaixo). Um aplicativo cliente ou DLL vincula dinamicamente as DLLs que ele requer. As interfaces através do limite application/DLL são interfaces de classe C++/MFC. O aplicativo cliente DEVE ser um aplicativo MFC. Esta DLL suporta todas as funcionalidades do MFC 3.0 (exceção: UNICODE não é suportado para as classes de base de dados).

Observação

A partir do Visual C++ versão 4.0, esse tipo de DLL é referido como uma "DLL de extensão".

Esta nota usará MFCxx.DLL para referir-se a todo o conjunto de DLL MFC, que inclui:

  • Depuração: MFCxxD.DLL (combinado) e MFCSxxD.LIB (estático).

  • Lançamento: MFCxx.DLL (combinado) e MFCSxx.LIB (estático).

  • Depuração Unicode: MFCxxUD.DLL (combinado) e MFCSxxD.LIB (estático).

  • Versão Unicode: MFCxxU.DLL (combinado) e MFCSxxU.LIB (estático).

Observação

As MFCSxx[U][D].LIB bibliotecas são usadas em conjunto com as DLLs compartilhadas do MFC. Essas bibliotecas contêm código que deve ser vinculado estaticamente ao aplicativo ou DLL.

Um aplicativo vincula-se às bibliotecas de importação correspondentes:

  • Depuração: MFCxxD.LIB

  • Lançamento: MFCxx.LIB

  • Depuração Unicode: MFCxxUD.LIB

  • Versão Unicode: MFCxxU.LIB

Uma DLL de extensão MFC é uma DLL que amplia MFCxx.DLL (ou as outras DLLs partilhadas MFC). Aqui, a arquitetura de componentes MFC entra em ação. Se você derivar uma classe útil de uma classe MFC, ou criar outro kit de ferramentas semelhante ao MFC, você pode colocá-lo em uma DLL. Sua DLL usa MFCxx.DLL, assim como o aplicativo cliente final. Uma DLL de extensão MFC permite classes folha reutilizáveis, classes base reutilizáveis e classes de visualização e documento reutilizáveis.

Prós e contras

Por que você deve usar a versão compartilhada do MFC

  • O uso da biblioteca compartilhada pode resultar em aplicativos menores. (Um aplicativo mínimo que usa a maior parte da biblioteca MFC é inferior a 10K).

  • A versão compartilhada do MFC suporta DLLs de extensão MFC e DLLs MFC regulares.

  • É mais rápido criar um aplicativo que usa as bibliotecas MFC compartilhadas do que um aplicativo MFC vinculado estaticamente. Isso porque não é necessário vincular o MFC em si. É especialmente verdadeiro em DEBUG compilações onde o vinculador deve compactar as informações de depuração. Quando seu aplicativo é vinculado a uma DLL que já contém as informações de depuração, há menos informações de depuração para compactar.

Por que você não deve usar a versão compartilhada do MFC:

  • O envio de um aplicativo que usa a biblioteca compartilhada requer que você envie MFCxx.DLL e outras bibliotecas com seu programa. MFCxx.DLL é livremente redistribuível como muitas DLLs, mas você ainda deve instalar a DLL em seu programa de instalação. Além disso, você terá que enviar as outras bibliotecas redistribuíveis usadas pelo seu programa e as próprias DLLs MFC.

Como escrever uma DLL de extensão MFC

Uma DLL de extensão MFC é uma DLL que contém classes e funções para expandir a funcionalidade das classes MFC. Uma DLL de extensão MFC usa as DLLs MFC compartilhadas da mesma forma que um aplicativo as usa, com algumas considerações adicionais:

  • O processo de compilação é semelhante à criação de um aplicativo que usa as bibliotecas MFC compartilhadas com algumas opções adicionais de compilador e vinculador.

  • Uma DLL de extensão MFC não tem uma CWinAppclasse derivada.

  • Uma DLL de extensão MFC deve fornecer um DllMain especial. O AppWizard fornece uma DllMain função que você pode modificar.

  • Uma DLL de extensão MFC normalmente fornece uma rotina de inicialização para criar um CDynLinkLibrary, se a DLL de extensão MFC exportar CRuntimeClass tipos ou recursos para o aplicativo. Uma classe derivada de CDynLinkLibrary pode ser usada se for necessário manter os dados específicos de cada aplicação na DLL de extensão MFC.

Estas considerações são descritas mais pormenorizadamente a seguir. Consulte também o exemplo DLLHUSKde MFC Advanced Concepts . Mostra como:

  • Crie um aplicativo usando as bibliotecas compartilhadas. DLLHUSK.EXE( é um aplicativo MFC que se vincula dinamicamente às bibliotecas MFC e outras DLLs.)

  • Crie uma DLL de extensão MFC. (Ele mostra como sinalizadores especiais, como _AFXEXT são usados ao criar uma DLL de extensão MFC.)

  • Crie dois exemplos de DLLs de extensão MFC. Um mostra a estrutura básica de uma DLL de extensão MFC com exportações limitadas (TESTDLL1) e o outro mostra a exportação de uma interface de classe inteira (TESTDLL2).

Tanto o aplicativo cliente quanto qualquer DLLs de extensão MFC devem usar a mesma versão do MFCxx.DLL. Siga as convenções das DLLs MFC e forneça versões de depuração e de lançamento (/release) da sua DLL de extensão MFC. Essa prática permite que os programas cliente criem versões de depuração e lançamento de seus aplicativos e as vinculem à depuração apropriada ou à versão de lançamento de todas as DLLs.

Observação

Como problemas de gerenciamento 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 lançamento da mesma DLL e DLLs para plataformas diferentes. A versão MFCxx.DLL tem cerca de 2000 pontos de entrada exportados; a versão de depuração MFCxxD.DLL tem cerca de 3000 pontos de entrada exportados.

Nota rápida sobre o gerenciamento de memória

A seção intitulada "Gerenciamento de memória", perto do final desta nota técnica, descreve a implementação do MFCxx.DLL com a versão compartilhada do MFC. As informações que você precisa saber para implementar apenas uma DLL de extensão MFC são descritas aqui.

MFCxx.DLL e todas as DLLs de extensão MFC carregadas no espaço de endereçamento de um aplicativo cliente usarão o mesmo alocador de memória, carregamento de recursos e outros estados "globais" do MFC como se estivessem no mesmo aplicativo. É significativo porque as bibliotecas de DLL não-MFC e as DLLs MFC regulares que se vinculam estaticamente ao MFC fazem exatamente o oposto: cada DLL aloca fora de seu próprio pool de memória.

Se uma DLL de extensão MFC aloca memória, então essa memória pode se misturar livremente com qualquer outro objeto alocado pelo aplicativo. Além disso, se um aplicativo que usa as bibliotecas MFC compartilhadas falhar, o sistema operacional mantém a integridade de qualquer outro aplicativo MFC que compartilha a DLL.

Da mesma forma, outros estados MFC "globais", como o arquivo executável atual para carregar recursos, também são compartilhados entre o aplicativo cliente, todas as DLLs de extensão MFC e MFCxx.DLL ele próprio.

Construindo uma DLL de extensão MFC

Você pode usar o AppWizard para criar um projeto DLL de extensão MFC e ele gera automaticamente as configurações apropriadas do compilador e do vinculador. Ele também gera uma DllMain função que você pode modificar.

Se você estiver convertendo um projeto existente em uma DLL de extensão MFC, comece com as configurações padrão que compilam usando a versão compartilhada do MFC. Em seguida, faça as seguintes alterações:

  • Adicione /D_AFXEXT aos sinalizadores do compilador. Na caixa de diálogo Propriedades do projeto, selecione a categoria C/C++>Preprocessor . Adicione _AFXEXT ao campo Definir macros , separando cada um dos itens com ponto-e-vírgula.

  • Remova a opção de compilação /Gy. Na caixa de diálogo Propriedades do projeto, selecione a categoria Geração de código>. Verifique se a propriedade Habilitar Function-Level Vinculação não está habilitada. Essa configuração facilita a exportação de classes, porque o vinculador não removerá funções não referenciadas. Se o projeto original criou uma DLL MFC regular que está estaticamente vinculada ao MFC, altere a /MT opção do compilador (ou /MTd) para /MD (ou /MDd).

  • Crie uma biblioteca de exportação com a opção /DLL LINK. Essa opção é definida quando você cria um novo destino e especifica Win32 Dynamic-Link Library como o tipo de destino.

Alterando seus arquivos de cabeçalho

O objetivo habitual de uma DLL de extensão MFC é exportar alguma funcionalidade comum para um ou mais aplicativos que podem usar essa funcionalidade. Essencialmente, a DLL exporta classes e funções globais para uso por seus aplicativos cliente.

Para garantir que cada função de membro seja marcada para importação ou exportação, conforme apropriado, use as especiais declarações __declspec(dllexport) e __declspec(dllimport). Quando os aplicativos cliente usam suas classes, você deseja que elas sejam declaradas como __declspec(dllimport). Quando a própria DLL de extensão MFC é criada, as funções devem ser declaradas como __declspec(dllexport). A DLL construída também deve exportar as funções, para que os programas cliente possam se vincular a elas no momento do carregamento.

Para exportar sua classe inteira, use AFX_EXT_CLASS na definição de classe. A estrutura define essa macro como __declspec(dllexport) quando _AFXDLL e _AFXEXT é definida, mas a define como __declspec(dllimport) quando _AFXEXT não está definida. _AFXEXT só é definido ao criar sua DLL de extensão MFC. Por exemplo:

class AFX_EXT_CLASS CExampleExport : public CObject
{ /* ... class definition ... */ };

Não exportar a classe inteira

Às vezes, você pode querer exportar apenas os membros individuais necessários da sua classe. Por exemplo, se você exportar uma CDialogclasse derivada, talvez só precise exportar o construtor e a DoModal chamada. Você pode exportar esses membros usando o arquivo DEF da DLL, mas você também pode usar AFX_EXT_CLASS da mesma maneira nos membros individuais que você precisa exportar.

Por exemplo:

class CExampleDialog : public CDialog
{
public:
    AFX_EXT_CLASS CExampleDialog();
    AFX_EXT_CLASS int DoModal();
    // rest of class definition
    // ...
};

Quando o fizer, poderá ter um problema adicional porque não exporta todos os membros da classe. O problema está na forma como as macros MFC funcionam. Várias das macros auxiliares do MFC realmente declaram ou definem membros de dados. A sua DLL também precisa exportar esses componentes de dados.

Por exemplo, a macro DECLARE_DYNAMIC é definida da seguinte forma ao criar uma DLL de extensão MFC:

#define DECLARE_DYNAMIC(class_name) \
protected: \
    static CRuntimeClass* PASCAL _GetBaseClass(); \
    public: \
    static AFX_DATA CRuntimeClass class##class_name; \
    virtual CRuntimeClass* GetRuntimeClass() const; \

A linha que começa static AFX_DATA declara um objeto estático dentro da sua classe. Para exportar essa classe corretamente e acessar as informações de tempo de execução de um cliente EXE, você precisa exportar esse objeto estático. Como o objeto estático é declarado com o modificador AFX_DATA, você só precisa definir AFX_DATA como __declspec(dllexport) quando você cria sua DLL. Defina-o como __declspec(dllimport) quando você cria o executável do cliente.

Como discutido acima, AFX_EXT_CLASS já está definido desta forma. Você só precisa redefinir AFX_DATA para ser o mesmo que AFX_EXT_CLASS em torno de sua definição de classe.

Por exemplo:

#undef  AFX_DATA
#define AFX_DATA AFX_EXT_CLASS
class CExampleView : public CView
{
    DECLARE_DYNAMIC()
    // ... class definition ...
};
#undef  AFX_DATA
#define AFX_DATA

MFC sempre usa o AFX_DATA símbolo em itens de dados que define dentro de suas macros, portanto, essa técnica funcionará para todos esses cenários. Por exemplo, funcionará para DECLARE_MESSAGE_MAP.

Observação

Se você estiver exportando a classe inteira em vez de membros selecionados da classe, os membros de dados estáticos serão exportados automaticamente.

Você pode usar a mesma técnica para exportar automaticamente o CArchive operador de extração para classes que usam as macros DECLARE_SERIAL e IMPLEMENT_SERIAL. Exporte o operador de arquivo entre parênteses as declarações de classe (localizadas no arquivo de cabeçalho) com o seguinte código:

#undef AFX_API
#define AFX_API AFX_EXT_CLASS

/* your class declarations here */

#undef AFX_API
#define AFX_API

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) exportá-lo de uma DLL e importá-lo __declspec(dllimport) de uma DLL. Ao definir _AFXEXT, os cabeçalhos MFC asseguram que AFX_EXT_CLASS está corretamente definido.

Quando você tem várias camadas, um símbolo como AFX_EXT_CLASS não é suficiente: uma DLL de extensão MFC pode exportar suas próprias classes e também importar outras classes de outra DLL de extensão MFC. Para lidar com esse problema, use um símbolo de pré-processador especial que indica que você está criando a própria DLL, em vez de usar a DLL. Por exemplo, imagine duas DLLs de extensão MFC, A.DLLe B.DLL. Cada um deles exporta algumas classes em A.H e B.H, respectivamente. 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, é construído com /DA_IMPL e quando B.DLL é construído, é construído com /DB_IMPL. Usando símbolos separados de cada DLL, CExampleB é exportado e CExampleA é importado ao construir B.DLL. CExampleA é exportado quando construído 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 incorporados do pré-processador AFX_EXT_CLASS e _AFXEXT. A técnica descrita acima resolve este problema da mesma forma que o MFC. MFC usa essa técnica ao criar suas DLLs de extensão OLE, Banco de Dados e Rede MFC.

Ainda não se exporta a classe inteira

Novamente, você terá que ter um cuidado especial quando não estiver exportando uma classe inteira. Certifique-se de que os itens de dados necessários criados pelas macros MFC são exportados corretamente. Você pode fazer isso redefinindo AFX_DATA para a macro da sua classe específica. Redefina sempre que 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

DllMain

Aqui está o código que você deve colocar em seu arquivo de origem principal para sua extensão MFC DLL. Deve vir depois das inclusões padrão. Quando você usa o AppWizard para criar arquivos iniciais para uma DLL de extensão MFC, ele fornece um DllMain para você.

#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
}

A chamada para AfxInitExtensionModule captura as runtime-classes do módulo (CRuntimeClass estruturas) e as fábricas de objetos do módulo (COleObjectFactory objetos) para uso posterior quando o objeto CDynLinkLibrary é criado. A chamada (opcional) para AfxTermExtensionModule permite que MFC limpe a DLL de extensão MFC quando cada processo é desanexado (o que acontece quando o processo é encerrado ou quando a DLL é descarregada por uma FreeLibrary chamada) da DLL de extensão MFC. Como a maioria das DLLs de extensão MFC não são carregadas dinamicamente (normalmente, elas são vinculadas por meio de suas bibliotecas de importação), a chamada para AfxTermExtensionModule geralmente não é necessária.

Se o seu aplicativo carrega e libera DLLs de extensão MFC dinamicamente, certifique-se de chamar AfxTermExtensionModule como mostrado acima. Certifique-se também de usar AfxLoadLibrary e AfxFreeLibrary (em vez de funções LoadLibrary Win32 e FreeLibrary) se seu aplicativo usa vários threads ou se ele carrega dinamicamente uma DLL de extensão MFC. Usando AfxLoadLibrary e AfxFreeLibrary garante que o código de inicialização e desligamento que é executado quando a DLL de extensão MFC é carregada e descarregada não corrompe o estado MFC global.

O arquivo AFXDLLX.H de cabeçalho contém definições especiais para estruturas usadas em DLLs de extensão MFC, como a definição de AFX_EXTENSION_MODULE e CDynLinkLibrary.

A extensionDLL global deve ser declarada conforme mostrado. Ao contrário da versão de 16 bits do MFC, pode alocar memória e chamar funções MFC durante este tempo, uma vez que o MFCxx.DLL é totalmente inicializado quando a sua DllMain é chamada.

Compartilhando recursos e classes

DLLs de extensão MFC simples só precisam exportar algumas funções de baixa largura de banda para o aplicativo cliente e nada mais. DLLs mais intensivas na interface do usuário podem querer exportar recursos e classes C++ para o aplicativo cliente.

A exportação de recursos é feita através de uma lista de recursos. Em cada aplicativo há uma lista de CDynLinkLibrary objetos vinculados individualmente. Ao procurar um recurso, a maioria das implementações MFC padrão que carregam recursos examinam primeiro o módulo de recurso atual (AfxGetResourceHandle) e, se não forem encontradas, percorrem a lista de CDynLinkLibrary objetos que tentam carregar o recurso solicitado.

A criação dinâmica de objetos C++ com um nome de classe C++ é semelhante. O mecanismo de desserialização de objeto MFC precisa ter todos os CRuntimeClass objetos registrados para que possa reconstruir criando dinamicamente o objeto C++ do tipo necessário com base no que foi armazenado anteriormente.

Se você quiser que o aplicativo cliente use classes em sua DLL de extensão MFC que são DECLARE_SERIAL, então você precisará exportar suas classes para torná-las visíveis para o aplicativo cliente. Também é feito andando pela lista CDynLinkLibrary.

No exemplo DLLHUSKde Conceitos Avançados do MFC, a lista tem a seguinte aparência:

head ->   DLLHUSK.EXE   - or - DLLHUSK.EXE
               |                    |
          TESTDLL2.DLL         TESTDLL2.DLL
               |                    |
          TESTDLL1.DLL         TESTDLL1.DLL
               |                    |
               |                    |
           MFC90D.DLL           MFC90.DLL

A MFCxx.DLL entrada geralmente vem em último lugar na lista de recursos e classes. MFCxx.DLL inclui todos os recursos padrão do MFC, incluindo cadeias de prompts para todos os IDs de comando padrão. Colocá-lo no final da lista permite que as DLLs e o próprio aplicativo cliente confiem nos recursos compartilhados no MFCxx.DLL, em vez de ter suas próprias cópias.

Mesclar os recursos e nomes de classe de todas as DLLs no espaço de nome do aplicativo cliente tem a desvantagem de que você precisa ter cuidado com quais IDs ou nomes você escolhe. Você pode desabilitar esse recurso não exportando seus recursos ou um CDynLinkLibrary objeto para o aplicativo cliente. O DLLHUSK exemplo gerencia o espaço de nome de recurso compartilhado usando vários arquivos de cabeçalho. Consulte a Nota Técnica 35 para obter mais dicas sobre como usar arquivos de recursos compartilhados.

Inicializando a DLL

Como mencionado acima, você geralmente desejará criar um CDynLinkLibrary objeto para exportar seus recursos e classes para o aplicativo cliente. Você precisará fornecer um ponto de entrada exportado para inicializar a DLL. Minimamente, é uma void rotina que não aceita argumentos e não devolve nada, mas pode ser o que desejar.

Cada aplicativo cliente que deseja usar sua DLL deve chamar essa rotina de inicialização, se você usar essa abordagem. Você também pode alocar este CDynLinkLibrary objeto no seu DllMain logo após chamar AfxInitExtensionModule.

A rotina de inicialização deve criar um CDynLinkLibrary objeto no heap do aplicativo atual, conectado às informações da DLL da extensão MFC. Você pode fazer isso definindo uma função como esta:

extern "C" extern void WINAPI InitXxxDLL()
{
    new CDynLinkLibrary(extensionDLL);
}

O nome da rotina, InitXxxDLL neste exemplo, pode ser o que você quiser. Não precisa ser extern "C", mas torna a lista de exportação mais fácil de manter.

Observação

Se utilizares a tua DLL de extensão MFC a partir de uma DLL MFC regular, deves exportar esta função de inicialização. Esta função deve ser chamada primeiro na DLL MFC regular, antes de utilizar qualquer classe ou recurso de extensão DLL MFC.

Exportando entradas

A maneira simples de exportar suas classes é usar __declspec(dllimport) e __declspec(dllexport) em cada classe e função global que você deseja exportar. É muito mais fácil, mas é menos eficiente do que nomear cada ponto de entrada em um arquivo DEF, conforme descrito abaixo. Isso porque você tem menos controle sobre quais funções são exportadas. E não é possível exportar as funções por ordinal. TESTDLL1 e TESTDLL2 usam este método para exportar as suas entradas.

Um método mais eficiente é exportar cada entrada nomeando-a no arquivo DEF. Este método é usado por MFCxx.DLL. Como estamos exportando seletivamente de nossa DLL, devemos decidir quais interfaces específicas desejamos exportar. É difícil, uma vez que você deve especificar os nomes distorcidos para o vinculador na forma de entradas no arquivo DEF. Não exporte nenhuma classe C++, a menos que você realmente precise ter um link simbólico para ela.

Se você já tentou exportar classes C++ com um arquivo DEF antes, convém desenvolver uma ferramenta para gerar essa lista automaticamente. Isso pode ser feito usando um processo de link de duas etapas. Vincule sua DLL uma vez sem exportações e permita que o vinculador gere um arquivo MAP. O arquivo MAP contém uma lista de funções que devem ser exportadas. Com alguma reorganização, você pode usá-lo para gerar as entradas EXPORT para seu arquivo DEF. A lista de exportação para MFCxx.DLL e as DLLs de extensão OLE e Database MFC, vários milhares em número, foi gerada com esse processo (embora não seja totalmente automático e exija algum ajuste manual de vez em quando).

CWinApp vs. CDynLinkLibrary

Uma DLL de extensão MFC não tem um CWinAppobjeto derivado próprio. Em vez disso, deve trabalhar com o objeto derivado do CWinApp do aplicativo cliente. Isso significa que o aplicativo cliente possui a bomba de mensagem principal, o loop ocioso e assim por diante.

Se a sua DLL de extensão MFC precisa manter dados extras para cada aplicativo, pode derivar uma nova classe a partir de CDynLinkLibrary e criá-la na rotina InitXxxDLL descrita acima. Durante a execução, a DLL pode verificar a lista de objetos do aplicativo atual para encontrar aquele associado à DLL de extensão MFC específica CDynLinkLibrary.

Usando recursos em sua implementação de DLL

Como mencionado acima, a carga de recurso padrão percorrerá a lista de CDynLinkLibrary objetos procurando o primeiro EXE ou DLL que tenha o recurso solicitado. Todas as APIs MFC e todo o código interno usa AfxFindResourceHandle para percorrer a lista de recursos para encontrar qualquer recurso, não importa onde ele esteja localizado.

Se você deseja carregar apenas recursos de um local específico, use as APIs AfxGetResourceHandle e AfxSetResourceHandle salve o identificador antigo e defina o novo identificador. Certifique-se de restaurar o identificador de recurso antigo antes de retornar ao aplicativo cliente. O exemplo TESTDLL2 usa essa abordagem para carregar explicitamente um menu.

Percorrer a lista tem algumas desvantagens: é um pouco mais lento e requer o gerenciamento de intervalos de identificadores de recursos. Ele tem a vantagem de que um aplicativo cliente que se vincula a várias DLLs de extensão MFC pode usar qualquer recurso fornecido pela DLL sem ter que especificar o identificador de instância da DLL. AfxFindResourceHandle é uma API usada para percorrer a lista de recursos para procurar uma determinada correspondência. Ele usa o nome e o tipo de um recurso e retorna o identificador de recurso onde ele encontra o recurso pela primeira vez, ou NULL.

Escrevendo um aplicativo que usa a versão DLL

Requisitos de candidatura

Um aplicativo que usa a versão compartilhada do MFC deve seguir algumas regras básicas:

  • Ele deve ter um CWinApp objeto e seguir as regras padrão para uma bomba de mensagem.

  • Ele deve ser compilado com um conjunto de sinalizadores de compilador necessários (veja abaixo).

  • Ele deve se vincular com as bibliotecas de importação MFCxx. Ao definir os sinalizadores de compilador necessários, os cabeçalhos MFC determinam no momento do link com qual biblioteca o aplicativo deve se vincular.

  • Para executar o executável, MFCxx.DLL deve estar no caminho ou no diretório do sistema Windows.

Construindo com o Ambiente de Desenvolvimento

Se você estiver usando o makefile interno com a maioria dos padrões padrão, você pode facilmente alterar o projeto para construir a versão DLL.

A etapa a seguir pressupõe que você tenha um aplicativo MFC funcionando corretamente vinculado a NAFXCWD.LIB (para depuração) e NAFXCW.LIB (para lançamento) e deseja convertê-lo para usar a versão compartilhada da biblioteca MFC. Você está executando o ambiente do Visual Studio e tem um arquivo de projeto interno.

  1. No menu Projetos , selecione Propriedades. Na página Geral em Padrões do projeto, defina Microsoft Foundation Classes para usar MFC em uma DLL compartilhada (MFCxx(d).dll).

Construindo com NMAKE

Se você estiver usando o recurso makefile externo do compilador, ou estiver usando NMAKE diretamente, você terá que editar seu makefile para suportar as opções necessárias do compilador e do vinculador.

Sinalizadores de compilador necessários:

  • /D_AFXDLL /MD /D_AFXDLL

Os cabeçalhos MFC padrão precisam que o _AFXDLL símbolo seja definido.

  • /MD O aplicativo deve usar a versão DLL da biblioteca de tempo de execução C.

Todos os outros sinalizadores do compilador seguem os padrões do MFC (por exemplo, _DEBUG para depuração).

Edite a lista de vinculadores de bibliotecas. Alterar NAFXCWD.LIB para MFCxxD.LIB e alterar NAFXCW.LIB para MFCxx.LIB. Substituir LIBC.LIB por MSVCRT.LIB. Como acontece com qualquer outra biblioteca MFC, é importante que MFCxxD.LIB seja colocado antes de qualquer biblioteca C-runtime.

Opcionalmente, adicione /D_AFXDLL às opções do compilador de recursos de versão e depuração (aquele que realmente compila os recursos com /R). Essa opção torna seu executável final menor compartilhando os recursos presentes nas DLLs MFC.

Uma reconstrução completa é necessária depois que essas alterações são feitas.

Construindo as amostras

A maioria dos programas de exemplo MFC pode ser criada a partir do Visual C++ ou de um MAKEFILE compartilhado compatível com NMAKE a partir da linha de comando.

Para converter qualquer um desses exemplos para usar MFCxx.DLL, você pode carregar o arquivo MAK no Visual C++ e definir as opções do projeto conforme descrito acima. Se você estiver usando a compilação NMAKE, você pode especificar AFXDLL=1 na linha de comando NMAKE e que irá criar o exemplo usando as bibliotecas MFC compartilhadas.

O exemplo de conceitos avançados do MFC DLLHUSK é criado com a versão DLL do MFC. Este exemplo não apenas ilustra como criar um aplicativo vinculado ao MFCxx.DLL, mas também ilustra outros recursos da opção de empacotamento MFC DLL, como DLLs de extensão MFC descritas posteriormente nesta nota técnica.

Notas de embalagem

As versões de lançamento das DLLs (MFCxx.DLL e MFCxxU.DLL) são livremente redistribuíveis. As versões de depuração das DLLs não são livremente redistribuíveis e devem ser usadas apenas durante o desenvolvimento do seu aplicativo.

As DLLs de debug são fornecidas com informações de depuração. Usando o depurador do Visual C++, você pode rastrear a execução do seu aplicativo e da DLL. As DLLs de lançamento (MFCxx.DLL e MFCxxU.DLL) não contêm informações de depuração.

Se você personalizar ou reconstruir as DLLs, então você deve chamá-las de algo diferente de "MFCxx". O arquivo MFCDLL.MAK MFC SRC descreve as opções de compilação e contém a lógica para renomear a DLL. Renomear os arquivos é necessário, uma vez que essas DLLs são potencialmente compartilhadas por muitos aplicativos MFC. A substituição das DLLs MFC instaladas no sistema pela sua versão personalizada pode afetar negativamente outro aplicativo MFC que utilize as DLLs MFC compartilhadas.

Não é recomendado reconstruir as DLLs MFC.

Como o MFCxx.DLL é implementado

A seção a seguir descreve como a DLL MFC (MFCxx.DLL e MFCxxD.DLL) é implementada. Entender os detalhes aqui também não são importantes se tudo o que você deseja fazer é usar a DLL MFC com seu aplicativo. Os detalhes aqui não são essenciais para entender como escrever uma DLL de extensão MFC, mas entender essa implementação pode ajudá-lo a escrever sua própria DLL.

Visão geral da implementação

A DLL MFC é realmente um caso especial de uma DLL de extensão MFC como descrito acima. Tem um grande número de exportações para um grande número de classes. Há algumas coisas adicionais que fazemos na DLL MFC que a tornam ainda mais especial do que uma DLL de extensão MFC regular.

Win32 faz a maior parte do trabalho

A versão de 16 bits do MFC precisava de uma série de técnicas especiais, incluindo dados específicos de cada aplicativo no segmento de pilha, segmentos especiais criados por algum código de assembly 80x86, contextos de exceção por processo e outras técnicas. Win32 suporta diretamente dados por processo em uma DLL, que é o que você deseja na maioria das vezes. Na maioria das vezes MFCxx.DLL é apenas NAFXCW.LIB empacotado numa DLL. Se você olhar para o código-fonte do MFC, encontrará poucos #ifdef _AFXDLL casos, já que não há muitos casos especiais que precisam ser feitos. Os casos especiais que existem são especificamente para lidar com Win32 no Windows 3.1 (também conhecido como Win32s). Win32s não suporta diretamente dados de DLL por processo. A MFC DLL deve utilizar as APIs do Win32 para o armazenamento local de threads (TLS) a fim de obter dados locais do processo.

Impacto nas fontes da biblioteca, arquivos adicionais

O impacto da versão _AFXDLL nas fontes normais e nos cabeçalhos da biblioteca de classes MFC é relativamente pequeno. Há um arquivo de versão especial (AFXV_DLL.H) e um arquivo de cabeçalho adicional (AFXDLL_.H) incluído pelo cabeçalho principal AFXWIN.H . O cabeçalho AFXDLL_.H inclui a classe CDynLinkLibrary e outros detalhes de implementação de aplicações _AFXDLL e DLLs de extensão MFC. O cabeçalho AFXDLLX.H é fornecido para construir DLLs de extensão MFC (veja acima para obter detalhes).

As fontes normais da biblioteca MFC no MFC SRC contêm um código condicional adicional definido pelo _AFXDLL #ifdef. Um arquivo de origem adicional (DLLINIT.CPP) contém o código adicional de inicialização de DLL e outro código auxiliar para a versão compartilhada do MFC.

Para construir a versão compartilhada do MFC, arquivos adicionais são fornecidos. (Veja abaixo para obter detalhes sobre como criar a DLL.)

  • Dois ficheiros DEF são usados para exportar os pontos de entrada da MFC DLL para as versões de depuração (MFCxxD.DEF) e de lançamento (MFCxx.DEF) da DLL.

  • Um arquivo RC (MFCDLL.RC) contém todos os recursos MFC padrão e um VERSIONINFO recurso para a DLL.

  • Um arquivo CLW (MFCDLL.CLW) é fornecido para permitir a navegação nas classes MFC usando ClassWizard. Esse recurso não é específico para a versão DLL do MFC.

Gerenciamento de memória

Um aplicativo usando MFCxx.DLL usa um alocador de memória comum fornecido pela MSVCRTxx.DLL, a DLL C-runtime compartilhada. A aplicação, as DLLs de extensão MFC, assim como as DLLs MFC, usam este alocador de memória partilhada. Usando uma DLL compartilhada para alocação de memória, as DLLs MFC podem alocar memória que é liberada posteriormente pelo aplicativo ou vice-versa. Como a aplicação e a DLL devem usar o mesmo alocador, você não deve substituir o C++ global operator new ou operator delete. As mesmas regras se aplicam ao restante das rotinas de alocação de memória em tempo de execução C (como malloc, realloc, freee outras).

Ordinais e classe __declspec(dllexport) e nomenclatura DLL

Não usamos a class__declspec(dllexport) funcionalidade do compilador C++. Em vez disso, uma lista de exportações é incluída com as fontes da biblioteca de classes (MFCxx.DEF e MFCxxD.DEF). Apenas um conjunto selecionado de pontos de entrada (funções e dados) é exportado. Outros símbolos, como funções ou classes de implementação privada MFC, não são exportados. Todas as exportações são feitas por ordinal, sem um nome de string na tabela de nomes residentes ou não residentes.

O uso class__declspec(dllexport) pode ser uma alternativa viável para a criação de DLLs menores, mas em uma DLL grande como MFC, o mecanismo de exportação padrão tem limites de eficiência e capacidade.

O que tudo isso significa é que podemos empacotar uma grande quantidade de funcionalidades na versão MFCxx.DLL que é de apenas cerca de 800 KB sem comprometer muita execução ou velocidade de carregamento. MFCxx.DLL teria sido 100 KB maior se esta técnica não tivesse sido usada. A técnica torna possível adicionar pontos de entrada adicionais no final do arquivo DEF. Ele permite um versionamento simples sem comprometer a velocidade e a eficiência de tamanho da exportação por ordem ordinal. As revisões principais de versão na biblioteca de classes MFC irão alterar o nome da biblioteca. Ou seja, MFC30.DLL é a DLL redistribuível que contém a versão 3.0 da biblioteca de classes MFC. Uma atualização dessa DLL, digamos, em um MFC 3.1 hipotético, a DLL seria nomeada MFC31.DLL em vez disso. Novamente, se você modificar o código-fonte MFC para produzir uma versão personalizada da DLL MFC, use um nome diferente (e de preferência um sem "MFC" no nome).

Ver também

Notas técnicas por número
Notas técnicas por categoria