Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Um arquivo executável vincula (ou carrega) uma DLL de duas maneiras:
Vinculação implícita, onde o sistema operacional carrega a DLL ao mesmo tempo que o executável que a usa. O executável do cliente chama as funções exportadas da DLL da mesma forma como se as funções estivessem estaticamente vinculadas e contidas no executável. A ligação implícita é por vezes referida como carga estática ou ligação dinâmica em tempo de carga.
Vinculação explícita, onde o sistema operacional carrega a DLL sob demanda em tempo de execução. Um executável que usa uma DLL por vinculação explícita deve carregar e descarregar explicitamente a DLL. Ele também deve configurar um ponteiro de função para aceder a cada função que utiliza a partir da DLL. Ao contrário das chamadas para funções em uma biblioteca vinculada estaticamente ou uma DLL vinculada implicitamente, o executável do cliente deve chamar as funções exportadas em uma DLL explicitamente vinculada por meio de ponteiros de função. A vinculação explícita às vezes é chamada de carga dinâmica ou vinculação dinâmica em tempo de execução.
Um executável pode usar qualquer método de vinculação para vincular à mesma DLL. Além disso, estes métodos não são mutuamente exclusivos; um executável pode ligar implicitamente a uma DLL, e outro pode anexar a ela explicitamente.
Determinar qual método de vinculação usar
Usar vinculação implícita ou explícita é uma decisão arquitetônica que você deve tomar para seu aplicativo. Existem vantagens e desvantagens para cada método.
Vinculação implícita
A vinculação implícita ocorre quando o código de um aplicativo chama uma função DLL exportada. Quando o código-fonte do executável de chamada é compilado ou montado, a chamada de função DLL gera uma referência de função externa no código-objeto. Para resolver essa referência externa, o aplicativo deve vincular com a biblioteca de importação (arquivo .lib) fornecida pelo fabricante da DLL.
A biblioteca de importação contém apenas código para carregar a DLL e implementar chamadas para funções na DLL. Localizar uma função externa em uma biblioteca de importação informa ao vinculador que o código para essa função está em uma DLL. Para resolver referências externas a DLLs, o vinculador simplesmente adiciona informações ao arquivo executável que informa ao sistema onde encontrar o código DLL quando o processo for iniciado.
Quando o sistema inicia um programa que contém referências vinculadas dinamicamente, ele usa as informações no arquivo executável do programa para localizar as DLLs necessárias. Se não conseguir localizar a DLL, o sistema encerra o processo e exibe uma caixa de diálogo que relata o erro. Caso contrário, o sistema mapeia os módulos DLL no espaço de endereçamento do processo.
Se qualquer uma das DLLs tiver uma função de ponto de entrada para código de inicialização e terminação, como DllMain, o sistema operacional chamará a função. Um dos parâmetros passados para a função de ponto de entrada especifica um código que indica que a DLL está anexada ao processo. Se a função de ponto de entrada não retornar TRUE, o sistema encerrará o processo e relatará o erro.
Finalmente, o sistema modifica o código executável do processo para fornecer endereços iniciais para as funções DLL.
Como o resto do código de um programa, o carregador mapeia o código DLL no espaço de endereço do processo quando o processo é iniciado. O sistema operacional o carrega na memória somente quando necessário. Como resultado, os PRELOAD atributos e LOADONCALL código usados pelos arquivos .def para controlar o carregamento em versões anteriores do Windows não têm mais significado.
Vinculação explícita
A maioria dos aplicativos usa vinculação implícita porque é o método de vinculação mais fácil de usar. No entanto, há momentos em que a ligação explícita é necessária. Aqui estão algumas razões comuns para usar links explícitos:
O aplicativo não sabe o nome de uma DLL que carrega até o tempo de execução. Por exemplo, o aplicativo pode obter o nome da DLL e as funções exportadas de um arquivo de configuração na inicialização.
Um processo que usa vinculação implícita é encerrado pelo sistema operacional se a DLL não for encontrada na inicialização do processo. Um processo que usa vinculação explícita não é encerrado nessa situação e pode tentar se recuperar do erro. Por exemplo, o processo pode notificar o usuário do erro e fazer com que o usuário especifique outro caminho para a DLL.
Um processo que usa vinculação implícita também é encerrado se qualquer uma das DLLs que está vinculada tiver uma
DllMainfunção que falha. Um processo que usa vinculação explícita não é encerrado nessa situação.Um aplicativo que implicitamente vincula a muitas DLLs pode ser lento para iniciar porque o Windows carrega todas as DLLs quando o aplicativo é carregado. Para melhorar o desempenho de inicialização, um aplicativo só pode usar vinculação implícita para DLLs necessárias imediatamente após o carregamento. Ele pode usar links explícitos para carregar outras DLLs somente quando forem necessárias.
A vinculação explícita elimina a necessidade de vincular o aplicativo usando uma biblioteca de importação. Se as alterações na DLL fizerem com que os ordinais de exportação sejam alterados, os aplicativos não precisarão ser revinculados se chamarem
GetProcAddressusando o nome de uma função e não um valor ordinal. Os aplicativos que usam vinculação implícita ainda devem ser revinculados à biblioteca de importação alterada.
Aqui estão dois perigos de ligações explícitas a ter em mente:
Se a DLL contiver uma função de entrada
DllMain, o sistema operacional chamará a função no contexto do encadeamento que invocouLoadLibrary. A função de ponto de entrada não é chamada se a DLL já estiver anexada ao processo, porque já houve uma chamada anterior paraLoadLibrarysem uma chamada correspondente para a funçãoFreeLibrary. A vinculação explícita pode causar problemas se a DLL usar umaDllMainfunção para inicializar cada thread de um processo, porque todos os threads que já existem quandoLoadLibrary(ouAfxLoadLibrary) é chamado não são inicializados.Se uma DLL declarar dados de extensão estática como
__declspec(thread), ela pode causar uma falha de proteção se explicitamente vinculada. Depois que a DLL é carregada por uma chamada paraLoadLibrary, ela causa uma falha de proteção sempre que o código faz referência a esses dados. (Os dados de extensão estática incluem itens estáticos globais e locais.) É por isso que, ao criar uma DLL, você deve evitar o uso de armazenamento local de thread. Se você não puder, informe seus usuários de DLL sobre as possíveis armadilhas de carregar dinamicamente sua DLL. Para obter mais informações, consulte Usando armazenamento local de segmento em uma biblioteca de ligação dinâmica (Windows SDK).
Como usar a vinculação implícita
Para usar uma DLL por vinculação implícita, os executáveis do cliente devem obter esses arquivos do provedor da DLL:
Um ou mais arquivos de cabeçalho (arquivos .h) que contêm as declarações dos dados exportados, funções e classes C++ na DLL. As classes, funções e dados exportados pela DLL devem ser marcados
__declspec(dllimport)no arquivo de cabeçalho. Para obter mais informações, consulte dllexport, dllimport.Uma biblioteca de importação para vincular ao seu executável. O vinculador cria a biblioteca de importação quando a DLL é criada. Para obter mais informações, consulte Ficheiros LIB como entrada do vinculador.
O arquivo DLL real.
Para usar os dados, funções e classes em uma DLL por vinculação implícita, qualquer arquivo de origem do cliente deve incluir os arquivos de cabeçalho que os declaram. Do ponto de vista da codificação, as chamadas para as funções exportadas são como qualquer outra chamada de função.
Para criar o arquivo executável do cliente, você deve vincular com a biblioteca de importação da DLL. Se você usar um makefile externo ou um sistema de compilação, especifique a biblioteca de importação juntamente com os outros arquivos de objeto ou bibliotecas vinculadas.
O sistema operacional deve ser capaz de localizar o arquivo DLL quando ele carrega o executável de chamada. Isso significa que você deve implantar ou verificar a existência da DLL quando você instala seu aplicativo.
Como vincular explicitamente a uma DLL
Para usar uma DLL por vinculação explícita, os aplicativos devem fazer uma chamada de função para carregar explicitamente a DLL em tempo de execução. Para vincular explicitamente a uma DLL, um aplicativo deve:
Chame LoadLibraryEx ou uma função semelhante para carregar a DLL e obter um identificador de módulo.
Chame GetProcAddress para obter um ponteiro de função para cada função exportada que a aplicação chama. Como os aplicativos chamam as funções DLL por meio de um ponteiro, o compilador não gera referências externas, portanto, não há necessidade de vincular a uma biblioteca de importação. No entanto, deve-se ter uma declaração
typedefouusingque defina a assinatura de chamada das funções exportadas que são chamadas.Chame FreeLibrary quando terminar com a DLL.
Por exemplo, esta função de exemplo chama LoadLibrary para carregar uma DLL chamada "MyDLL", chama GetProcAddress para obter um ponteiro para uma função chamada "DLLFunc1", chama a função e salva o resultado e, em seguida, chama FreeLibrary para descarregar a DLL.
#include "windows.h"
typedef HRESULT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT*);
HRESULT LoadAndCallSomeFunction(DWORD dwParam1, UINT * puParam2)
{
HINSTANCE hDLL; // Handle to DLL
LPFNDLLFUNC1 lpfnDllFunc1; // Function pointer
HRESULT hrReturnVal;
hDLL = LoadLibrary("MyDLL");
if (NULL != hDLL)
{
lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");
if (NULL != lpfnDllFunc1)
{
// call the function
hrReturnVal = lpfnDllFunc1(dwParam1, puParam2);
}
else
{
// report the error
hrReturnVal = ERROR_DELAY_LOAD_FAILED;
}
FreeLibrary(hDLL);
}
else
{
hrReturnVal = ERROR_DELAY_LOAD_FAILED;
}
return hrReturnVal;
}
Ao contrário deste exemplo, na maioria dos casos deve chamar LoadLibrary e FreeLibrary apenas uma vez na sua aplicação para uma DLL específica. É especialmente verdadeiro se você vai chamar várias funções na DLL, ou chamar funções DLL repetidamente.