O que é uma DLL

Este artigo descreve o que é uma DLL (biblioteca de vínculo dinâmico) e os vários problemas que podem ocorrer quando você usa DLLs. Ele também descreve alguns problemas avançados que você deve considerar ao desenvolver suas próprias DLLs.

Aplicável ao: Windows 10 - todas as edições
Número original do KB: 815065

Resumo

Ao descrever o que é uma DLL, este artigo descreve métodos de vinculação dinâmica, dependências de DLL, pontos de entrada de DLL, exportação de funções DLL e ferramentas de solução de problemas de DLL.

Este artigo termina com uma comparação de alto nível de DLLs com os assemblies .NET Framework Microsoft.

Para os sistemas operacionais Windows, grande parte da funcionalidade do sistema operacional é fornecida pela DLL. Além disso, quando você executa um programa em um desses sistemas operacionais Windows, grande parte da funcionalidade do programa pode ser fornecida por DLLs. Por exemplo, alguns programas podem conter muitos módulos diferentes e cada módulo do programa está contido e distribuído em DLLs.

O uso de DLLs ajuda a promover a modularização de código, reutilização de código, uso eficiente de memória e espaço em disco reduzido. Portanto, o sistema operacional e os programas são carregados e executados mais rapidamente e ocupam menos espaço em disco no computador.

Quando um programa usa uma DLL, um problema chamado dependência pode fazer com que o programa não seja executado. Quando um programa usa uma DLL, uma dependência é criada. Se outro programa substituir e interromper essa dependência, o programa original poderá não ser executado com êxito.

Com a introdução do .NET Framework, a maioria dos problemas de dependência foi eliminada usando assemblies.

Mais informações

Uma DLL é uma biblioteca que contém código e dados que podem ser usados por mais de um programa ao mesmo tempo. Por exemplo, em sistemas operacionais Windows, a DLL Comdlg32 executa funções comuns relacionadas à caixa de diálogo. Cada programa pode usar a funcionalidade contida nessa DLL para implementar uma caixa de diálogo Abrir. Isso ajuda a promover a reutilização de código e o uso eficiente de memória.

Usando uma DLL, um programa pode ser modularizado em componentes separados. Por exemplo, um programa de contabilidade pode ser vendido por módulo. Cada módulo poderá ser carregado no programa principal em tempo de execução se esse módulo estiver instalado. Como os módulos são separados, o tempo de carregamento do programa é mais rápido. E um módulo só é carregado quando essa funcionalidade é solicitada.

Além disso, as atualizações são mais fáceis de aplicar a cada módulo sem afetar outras partes do programa. Por exemplo, você pode ter um programa de folha de pagamento e as taxas de imposto mudam a cada ano. Quando essas alterações são isoladas em uma DLL, você pode aplicar uma atualização sem a necessidade de compilar ou instalar o programa inteiro novamente.

A lista a seguir descreve alguns dos arquivos implementados como DLLs em sistemas operacionais Windows:

  • Arquivos de Controles ActiveX (*.ocx)

    Um exemplo de controle ActiveX é um controle de calendário que permita selecionar uma data de calendário.

  • Arquivos do Painel de Controle (.cpl)

    Um exemplo de um arquivo .cpl é um item localizado em Painel de Controle. Cada item é uma DLL especializada.

  • Arquivos de driver de dispositivo (.drv)

    Um exemplo de driver de dispositivo é um driver de impressora que controla a impressão em uma impressora.

Vantagens da DLL

A lista a seguir descreve algumas das vantagens que são fornecidas quando um programa usa uma DLL:

  • Usa menos recursos

    Quando vários programas usam a mesma biblioteca de funções, uma DLL pode reduzir a duplicação de código que é carregada no disco e na memória física. Isso pode influenciar muito o desempenho não apenas do programa em execução em primeiro plano, mas também de outros programas que estão sendo executados no sistema operacional Windows.

  • Promove a arquitetura modular

    Uma DLL ajuda a promover o desenvolvimento de programas modulares. Ele ajuda você a desenvolver programas grandes que exigem várias versões de linguagem ou um programa que requer arquitetura modular. Um exemplo de programa modular é um programa de contabilidade que tem muitos módulos que podem ser carregados dinamicamente em tempo de execução.

  • Facilita a implantação e a instalação

    Quando uma função dentro de uma DLL precisa de uma atualização ou uma correção, a implantação e a instalação da DLL não exigem que o programa seja revinculado com a DLL. Além disso, se vários programas usarem a mesma DLL, todos os vários programas se beneficiarão da atualização ou da correção. Esse problema pode ocorrer com mais frequência quando você usa uma DLL de terceiros que é atualizada ou corrigida regularmente.

Dependências de DLL

Quando um programa ou uma DLL usa uma função DLL em outra DLL, uma dependência é criada. O programa não é mais autossuficiente e poderá ter problemas se a dependência for interrompida. Por exemplo, o programa poderá não ser executado se ocorrer uma das seguintes ações:

  • Uma DLL dependente é atualizada para uma nova versão.
  • Uma DLL dependente é fixa.
  • Uma DLL dependente é substituída por uma versão anterior.
  • Uma DLL dependente é removida do computador.

Essas ações são conhecidas como conflitos de DLL. Se a compatibilidade com versões anteriores não for imposta, o programa poderá não ser executado com êxito.

A lista a seguir descreve as alterações introduzidas no Windows 2000 e em sistemas operacionais Windows posteriores para ajudar a minimizar problemas de dependência:

  • Proteção de Arquivo do Windows

    Na Proteção de Arquivo do Windows, o sistema operacional impede que as DLLs do sistema sejam atualizadas ou excluídas por um agente não autorizado. Quando uma instalação do programa tenta remover ou atualizar uma DLL definida como uma DLL do sistema, a Proteção de Arquivo do Windows procurará uma assinatura digital válida.

  • DLLs privadas

    As DLLs privadas permitem isolar um programa de alterações feitas em DLLs compartilhadas. As DLLs privadas usam informações específicas da versão ou um arquivo .local vazio para aplicar a versão da DLL usada pelo programa. Para usar DLLs privadas, localize suas DLLs na pasta raiz do programa. Em seguida, para novos programas, adicione informações específicas de versão à DLL. Para programas antigos, use um arquivo .local vazio. Cada método instrui o sistema operacional a usar as DLLs privadas localizadas na pasta raiz do programa.

Ferramentas de solução de problemas de DLL

Várias ferramentas estão disponíveis para ajudá-lo a solucionar problemas de DLL. As ferramentas a seguir são algumas dessas ferramentas.

Dependency Walker

A ferramenta Dependency Walker pode verificar recursivamente todas as DLLs dependentes que são usadas por um programa. Quando você abre um programa no Dependency Walker, o Dependency Walker faz as seguintes verificações:

  • O Dependency Walker verifica se há DLLs ausentes.
  • O Dependency Walker verifica se há arquivos de programa ou DLLs inválidos.
  • O Dependency Walker verifica se as funções de importação e exportação correspondem.
  • O Dependency Walker verifica se há erros de dependência circular.
  • O Dependency Walker verifica se há módulos que não são válidos porque os módulos são para um sistema operacional diferente.

Ao usar o Dependency Walker, você pode documentar todas as DLLs usadas por um programa. Isso pode ajudar a evitar e corrigir problemas de DLL que podem ocorrer no futuro. O Dependency Walker está localizado no seguinte diretório quando você instala o Visual Studio 6.0:

drive\Program Files\Microsoft Visual Studio\Common\Tools

Solucionador de Problemas Universal da DLL

A ferramenta DUPS (Solucionador de Problemas Universal da DLL) é usada para auditar, comparar, documentar e exibir informações de DLL. A lista a seguir descreve os utilitários que compõem a ferramenta DUPS:

  • Dlister.exe

    Esse utilitário enumera todas as DLLs no computador e registra as informações em um arquivo de texto ou em um arquivo de banco de dados.

  • Dcomp.exe

    Esse utilitário compara as DLLs listadas em dois arquivos de texto e produz um terceiro arquivo de texto que contém as diferenças.

  • Dtxt2DB.exe

    Esse utilitário carrega os arquivos de texto criados usando o utilitário Dlister.exe e o utilitário Dcomp.exe para o banco de dados dllHell.

  • DlgDtxt2DB.exe

    Esse utilitário fornece uma versão de GUI (interface gráfica do usuário) do utilitário Dtxt2DB.exe.

Banco de dados de Ajuda da DLL

O banco de dados de Ajuda da DLL ajuda você a localizar versões específicas de DLLs instaladas pelos produtos de software da Microsoft.

Desenvolvimento de DLL

Esta seção descreve os problemas e os requisitos que você deve considerar ao desenvolver suas próprias DLLs.

Tipos de DLLs

Quando você carrega uma DLL em um aplicativo, dois métodos de vinculação permitem chamar as funções de DLL exportadas. Os dois métodos de vinculação são vinculação dinâmica em tempo de carregamento e vinculação dinâmica em tempo de execução.

Vinculação dinâmica em tempo de carregamento

Na vinculação dinâmica em tempo de carregamento, um aplicativo faz chamadas explícitas para funções de DLL exportadas, como funções locais. Para usar a vinculação dinâmica em tempo de carregamento, forneça um arquivo de cabeçalho (.h) e um arquivo de biblioteca de importação (.lib) ao compilar e vincular o aplicativo. Quando você fizer isso, o vinculador fornecerá ao sistema as informações necessárias para carregar a DLL e resolver os locais de função DLL exportados no tempo de carregamento.

Vinculação dinâmica em tempo de execução

Na vinculação dinâmica em tempo de execução, um aplicativo chama a função LoadLibrary ou a função LoadLibraryEx para carregar a DLL em tempo de execução. Depois que a DLL for carregada com êxito, você usará a função GetProcAddress para obter o endereço da função de DLL exportada que você deseja chamar. Ao usar a vinculação dinâmica em tempo de execução, você não precisa de um arquivo de biblioteca de importação.

A lista a seguir descreve os critérios do aplicativo para quando usar a vinculação dinâmica em tempo de carregamento e quando usar a vinculação dinâmica em tempo de execução:

  • Desempenho da inicialização

    Se o desempenho inicial da inicialização do aplicativo for importante, você deverá usar a vinculação dinâmica em tempo de execução.

  • Facilidade de uso

    Na vinculação dinâmica em tempo de carregamento, as funções de DLL exportadas são como funções locais. Isso facilita a chamada dessas funções.

  • Lógica do aplicativo

    Na vinculação dinâmica em tempo de execução, um aplicativo pode ramificar para carregar módulos diferentes conforme necessário. É importante quando você desenvolve versões de vários idiomas.

O ponto de entrada DLL

Ao criar uma DLL, você pode especificar uma função de ponto de entrada. A função de ponto de entrada é chamada quando processos ou threads se anexam à DLL ou se desanexam da DLL. Você pode usar a função de ponto de entrada para inicializar estruturas de dados ou destruir estruturas de dados conforme exigido pela DLL. Além disso, se o aplicativo for multithreaded, você poderá usar o TLS (armazenamento local de thread) para alocar memória privada para cada thread na função de ponto de entrada. O código a seguir é um exemplo da função de ponto de entrada de DLL.

BOOL APIENTRY DllMain(
HANDLE hModule,// Handle to DLL module
DWORD ul_reason_for_call,// Reason for calling function
LPVOID lpReserved ) // Reserved
{
    switch ( ul_reason_for_call )
    {
        case DLL_PROCESS_ATTACHED: // A process is loading the DLL.
        break;
        case DLL_THREAD_ATTACHED: // A process is creating a new thread.
        break;
        case DLL_THREAD_DETACH: // A thread exits normally.
        break;
        case DLL_PROCESS_DETACH: // A process unloads the DLL.
        break;
    }
    return TRUE;
}

Quando a função de ponto de entrada retornar um valor FALSE, o aplicativo não será iniciado se você estiver usando a vinculação dinâmica em tempo de carregamento. Se você estiver usando a vinculação dinâmica em tempo de execução, somente a DLL individual não será carregada.

A função de ponto de entrada só deve executar tarefas de inicialização simples e não deve chamar nenhuma outra função de carregamento ou encerramento de DLL. Por exemplo, na função de ponto de entrada, você não deve chamar direta ou indiretamente a função LoadLibrary ou a função LoadLibraryEx. Além disso, você não deve chamar a função FreeLibrary quando o processo está sendo encerrado.

Observação

Em aplicativos multithread, verifique se o acesso aos dados globais da DLL está sincronizado (thread-safe) para evitar possíveis dados corrompidos. Para fazer isso, use o TLS para fornecer dados exclusivos para cada thread.

Exportar funções DLL

Para exportar funções DLL, você pode adicionar uma palavra-chave de função às funções DLL exportadas ou criar um arquivo de definição de módulo (.def) que lista as funções de DLL exportadas.

Para usar uma palavra-chave de função, você deve declarar cada função que deseja exportar com a seguinte palavra-chave:
__declspec(dllexport)

Para usar funções de DLL exportadas no aplicativo, você deve declarar cada função que deseja importar com a seguinte palavra-chave: __declspec(dllimport)

Normalmente, você usaria um arquivo de cabeçalho que tem uma instrução de definição e uma instrução ifdef para separar a instrução de exportação e a instrução import.

Você também pode usar um arquivo de definição de módulo para declarar funções de DLL exportadas. Ao usar um arquivo de definição de módulo, você não precisa adicionar a palavra-chave da função às funções de DLL exportadas. No arquivo de definição do módulo, você declara a instrução LIBRARY e a instrução EXPORTS para a DLL. O código a seguir é um exemplo de arquivo de definição.

// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS HelloWorld

Exemplo de DLL e aplicativo

No Visual C++ 6.0, você pode criar uma DLL selecionando o tipo de projeto Biblioteca de Vínculo Dinâmico Win32 ou o tipo de projeto MFC AppWizard (dll).

O código a seguir é um exemplo de uma DLL que foi criada no Visual C++ usando o tipo de projeto Biblioteca de Vínculo Dinâmico Win32.

// SampleDLL.cpp
//

#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
)
{
    return TRUE;
}

void HelloWorld()
{
    MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}

// File: SampleDLL.h
//
#ifndef INDLL_H
    #define INDLL_H
    #ifdef EXPORTING_DLL
        extern __declspec(dllexport) void HelloWorld();
    #else
        extern __declspec(dllimport) void HelloWorld();
    #endif

#endif

O código a seguir é um exemplo de projeto de Aplicativo Win32 que chama a função DLL exportada na DLL sampleDLL.

// SampleApp.cpp
//
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HelloWorld();
    return 0;
}

Observação

Na vinculação dinâmica em tempo de carregamento, você deve vincular a biblioteca de importação SampleDLL.lib que é criada ao compilar o projeto SampleDLL.

Na vinculação dinâmica em tempo de execução, você usa um código semelhante ao código a seguir para chamar a função DLL SampleDLL.dll exportada.

...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;

hinstDLL = LoadLibrary("sampleDLL.dll");
if (hinstDLL != NULL)
{
    HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
    if (HelloWorld != NULL)
        (HelloWorld);
    fFreeDLL = FreeLibrary(hinstDLL);
}
...

Quando você compila e vincula o aplicativo SampleDLL, o sistema operacional Windows pesquisa a DLL sampleDLL nos seguintes locais nesta ordem:

  1. A pasta do aplicativo

  2. A pasta atual

  3. A pasta de sistema do Windows

    Observação

    A função GetSystemDirectory retorna o caminho da pasta de sistema do Windows.

  4. A pasta do Windows

    Observação

    A função GetWindowsDirectory retorna o caminho da pasta do Windows.

O assembly .NET Framework

Com a introdução do .NET e do .NET Framework, a maioria dos problemas associados às DLLs foi eliminada usando assemblies. Um assembly é uma unidade lógica de funcionalidade executada sob o controle do CLR (common language runtime) do .NET. Um assembly existe fisicamente como um arquivo .dll ou como um arquivo .exe. No entanto, internamente, um assembly é diferente de uma DLL do Microsoft Win32.

Um arquivo de assembly contém um manifesto de assembly, metadados de tipo, código MSIL (Microsoft intermediate language) e outros recursos. O manifesto do assembly contém os metadados do assembly que fornecem todas as informações necessárias para que um assembly seja autodescritivo. As seguintes informações estão incluídas no manifesto do assembly:

  • Nome do assembly
  • Informação da versão
  • Informações de cultura
  • Informações de nome forte
  • A lista de assembly de arquivos
  • Informações de referência de tipo
  • Informações de assembly referenciadas e dependentes

O código MSIL contido no assembly não pode ser executado diretamente. Em vez disso, a execução de código MSIL é gerenciada por meio do CLR. Por padrão, quando você cria um assembly, o assembly é privado para o aplicativo. Para criar um assembly compartilhado, é necessário atribuir um nome forte ao assembly e, em seguida, publicar o assembly no cache de assembly global.

A lista a seguir descreve alguns dos recursos dos assemblies em comparação com os recursos das DLLs do Win32:

  • Autodescrição

    Quando você cria um assembly, todas as informações necessárias para o CLR executar o assembly estão contidas no manifesto do assembly. O manifesto do assembly contém uma lista dos assemblies dependentes. Portanto, o CLR pode manter um conjunto consistente de assemblies que são usados no aplicativo. Em DLLs Win32, você não pode manter a consistência entre um conjunto de DLLs usadas em um aplicativo quando você usa DLLs compartilhadas.

  • Controle de Versão

    Em um manifesto do assembly, as informações de versão são registradas e impostas pelo CLR. Além disso, as políticas de versão permitem aplicar o uso específico da versão. Em DLLs Win32, o controle de versão não pode ser imposto pelo sistema operacional. Você deve verificar se as DLLs são compatíveis com versões anteriores.

  • Implantação lado a lado

    Os assemblies dão suporte à implantação lado a lado. Um aplicativo pode usar uma versão de um assembly e outro aplicativo pode usar uma versão diferente de um assembly. A partir do Windows 2000, a implantação lado a lado tem suporte na localização de DLLs na pasta do aplicativo. Além disso, a Proteção de Arquivo do Windows impede que as DLLs do sistema sejam substituídas por um agente não autorizado.

  • Autossuficiência e isolamento

    Um aplicativo que é desenvolvido usando um assembly pode ser autossuficiente e isolado de outros aplicativos em execução no computador. Esse recurso ajuda você a criar instalações de impacto zero.

  • Execução

    Um assembly é executado sob as permissões de segurança fornecidas no manifesto do assembly e que são controladas pelo CLR.

  • Independente de linguagem

    Um assembly pode ser desenvolvido usando qualquer uma das linguagens .NET com suporte. Por exemplo, você pode desenvolver um assembly no Microsoft Visual C# e, em seguida, usar o assembly em um projeto Visual Basic .NET.

Coleta de dados

Se você precisar de ajuda do suporte da Microsoft, recomendamos coletar as informações seguindo as etapas mencionadas em Coletar informações usando o TSS para problemas relacionados à implantação.

Referências