DLL - Guia Rápido

A vinculação dinâmica é um mecanismo que vincula aplicativos a bibliotecas em tempo de execução. As bibliotecas permanecem em seus próprios arquivos e não são copiadas para os arquivos executáveis ​​dos aplicativos. DLLs vinculam-se a um aplicativo quando o aplicativo é executado, em vez de quando ele é criado. DLLs podem conter links para outras DLLs.

Muitas vezes, as DLLs são colocadas em arquivos com extensões diferentes, como .EXE, .DRV ou .DLL.

Vantagens do DLL

Abaixo estão algumas vantagens de ter arquivos DLL.

Usa menos recursos

Os arquivos DLL não são carregados na RAM junto com o programa principal; eles não ocupam espaço, a menos que sejam necessários. Quando um arquivo DLL é necessário, ele é carregado e executado. Por exemplo, enquanto um usuário do Microsoft Word estiver editando um documento, o arquivo DLL da impressora não será necessário na RAM. Se o usuário decidir imprimir o documento, o aplicativo Word fará com que o arquivo DLL da impressora seja carregado e executado.

Promove arquitetura modular

Um DLL ajuda a promover o desenvolvimento de programas modulares. Ajuda a desenvolver programas grandes que requerem versões em vários idiomas ou um programa que requer arquitetura modular. Um exemplo de programa modular é um programa de contabilidade com muitos módulos que podem ser carregados dinamicamente em tempo de execução.

Ajuda de fácil implantação e instalação

Quando uma função em uma DLL precisa de uma atualização ou correção, a implantação e instalação da DLL não exige que o programa seja vinculado novamente à DLL. Além disso, se vários programas usarem a mesma DLL, todos eles se beneficiarão com a atualização ou correção. Esse problema pode ocorrer com mais freqüência quando você usa uma DLL de terceiros que é regularmente atualizada ou corrigida.

Aplicativos e DLLs podem se vincular a outras DLLs automaticamente, se a vinculação de DLL for especificada na seção IMPORTS do arquivo de definição de módulo como parte da compilação. Caso contrário, você pode carregá-los explicitamente usando a função LoadLibrary do Windows.

Arquivos DLL importantes

  • COMDLG32.DLL - Controla as caixas de diálogo.

  • GDI32.DLL - Contém várias funções para desenhar gráficos, exibir texto e gerenciar fontes.

  • KERNEL32.DLL - Contém centenas de funções para o gerenciamento de memória e diversos processos.

  • USER32.DLL- Contém várias funções de interface do usuário. Envolvidos na criação de janelas de programas e suas interações entre si.

Primeiro, discutiremos 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 de tempo de carregamento e
  • vinculação dinâmica em tempo de execução.

Link dinâmico de tempo de carregamento

Na vinculação dinâmica de tempo de carregamento, um aplicativo faz chamadas explícitas às funções 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 exportados da DLL no momento do carregamento.

Link dinâmico de tempo de execução

Na vinculação dinâmica de 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 é carregada com êxito, você usa a função GetProcAddress, para obter o endereço da função 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 escolher entre o link dinâmico do tempo de carregamento e o link dinâmico do tempo de execução:

  • Startup performance Observação: se o desempenho de inicialização inicial do aplicativo for importante, você deve usar o link dinâmico em tempo de execução.

  • Ease of use: Na vinculação dinâmica de tempo de carregamento, as funções DLL exportadas são como funções locais. Ajuda a chamar essas funções facilmente.

  • Application logic: No link dinâmico de tempo de execução, um aplicativo pode se ramificar para carregar diferentes módulos conforme necessário. Isso é importante ao desenvolver versões em vários idiomas.

O ponto de entrada DLL

Ao criar uma DLL, você pode especificar opcionalmente uma função de ponto de entrada. A função de ponto de entrada é chamada quando processos ou threads se anexam à DLL ou se desconectam da DLL. Você pode usar a função de ponto de entrada para inicializar ou destruir estruturas de dados conforme exigido pela DLL.

Além disso, se o aplicativo for multithread, você pode usar o armazenamento local de thread (TLS) para alocar memória que é particular para cada thread na função de ponto de entrada. O código a seguir é um exemplo da função de ponto de entrada DLL.

BOOL APIENTRY DllMain(
HANDLE hModule,	// Handle to DLL module DWORD ul_reason_for_call, 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 retorna um valor FALSE, o aplicativo não será iniciado se você estiver usando a vinculação dinâmica de tempo de carregamento. Se você estiver usando vinculação dinâmica em tempo de execução, apenas a DLL individual não será carregada.

A função de ponto de entrada deve realizar apenas 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 oLoadLibrary função ou o LoadLibraryExfunção. Além disso, você não deve ligar para oFreeLibrary função quando o processo está terminando.

WARNING: Em aplicativos multithread, certifique-se de que o acesso aos dados globais DLL esteja sincronizado (thread-safe) para evitar possível corrupção de dados. Para fazer isso, use o TLS para fornecer dados exclusivos para cada thread.

Exportando 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 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 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 com define declaração e um ifdef declaração para separar a declaração de exportação e a declaração de importação.

Você também pode usar um arquivo de definição de módulo para declarar funções DLL exportadas. Quando você usa um arquivo de definição de módulo, não é necessário adicionar a palavra-chave de função às funções DLL exportadas. No arquivo de definição do módulo, você declara oLIBRARY declaração e o EXPORTSdeclaração para a DLL. O código a seguir é um exemplo de arquivo de definição.

// SampleDLL.def
//
LIBRARY "sampleDLL"

EXPORTS
   HelloWorld

Escreva uma DLL de amostra

No Microsoft Visual C ++ 6.0, você pode criar uma DLL selecionando o Win32 Dynamic-Link Library tipo de projeto ou o MFC AppWizard (dll) tipo de projeto.

O código a seguir é um exemplo de uma DLL que foi criada no Visual C ++ usando o tipo de projeto Win32 Dynamic-Link Library.

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

Chamando uma DLL de amostra

O código a seguir é um exemplo de um 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;
}

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

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

...
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 procura a DLL SampleDLL nos seguintes locais nesta ordem:

  • A pasta do aplicativo

  • A pasta atual

  • A pasta do sistema Windows (o GetSystemDirectory função retorna o caminho da pasta do sistema Windows).

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

Para usar uma DLL, ela deve ser registrada, tendo as referências apropriadas inseridas no Registro. Às vezes acontece que uma referência do Registro é corrompida e as funções da DLL não podem mais ser usadas. A DLL pode ser registrada novamente abrindo Start-Run e inserindo o seguinte comando:

regsvr32 somefile.dll

Este comando assume que somefile.dll está em um diretório ou pasta que está no PATH. Caso contrário, o caminho completo para a DLL deve ser usado. Um arquivo DLL também pode ter o registro cancelado usando a opção "/ u" conforme mostrado abaixo.

regsvr32 /u somefile.dll

Isso pode ser usado para ativar e desativar um serviço.

Várias ferramentas estão disponíveis para ajudá-lo a solucionar problemas de DLL. Alguns deles são discutidos abaixo.

Dependency Walker

A ferramenta Dependency Walker (depends.exe) pode verificar recursivamente todas as DLLs dependentes usadas por um programa. Quando você abre um programa no Dependency Walker, o Dependency Walker executa as seguintes verificações:

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

Usando o Dependency Walker, você pode documentar todas as DLLs que um programa usa. Pode ajudar a prevenir e corrigir problemas de DLL que possam ocorrer no futuro. Dependency Walker está localizado no seguinte diretório quando você instala o Microsoft Visual Studio 6.0:

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

DLL Universal Problem Solver

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

  • Dlister.exe - Este 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 - Este 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 - Este utilitário carrega os arquivos de texto criados com o utilitário Dlister.exe e o utilitário Dcomp.exe no banco de dados dllHell.

  • DlgDtxt2DB.exe - Este utilitário fornece uma versão da interface gráfica do usuário (GUI) do utilitário Dtxt2DB.exe.

Lembre-se das seguintes dicas ao escrever uma DLL:

  • Use a convenção de chamada adequada (C ou stdcall).

  • Esteja ciente da ordem correta dos argumentos passados ​​para a função.

  • NUNCA redimensione arrays ou concatene strings usando os argumentos passados ​​diretamente para uma função. Lembre-se de que os parâmetros que você passa são dados do LabVIEW. Mudar o tamanho do array ou string pode resultar em uma falha ao sobrescrever outros dados armazenados na memória do LabVIEW. Você PODE redimensionar arrays ou concatenar strings se passar um LabVIEW Array Handle ou LabVIEW String Handle e estiver usando o compilador Visual C ++ ou o compilador Symantec para compilar sua DLL.

  • Ao passar strings para uma função, selecione o tipo correto de string para passar. Identificador de string C ou Pascal ou LabVIEW.

  • As strings Pascal são limitadas a 255 caracteres de comprimento.

  • As strings C são terminadas em NULL. Se sua função DLL retornar dados numéricos em um formato de string binária (por exemplo, via GPIB ou a porta serial), ela pode retornar valores NULL como parte da string de dados. Nesses casos, passar matrizes de inteiros curtos (8 bits) é mais confiável.

  • Se você estiver trabalhando com arrays ou strings de dados, SEMPRE passe um buffer ou array que seja grande o suficiente para conter quaisquer resultados colocados no buffer pela função, a menos que você os esteja passando como alças do LabVIEW, caso em que você pode redimensioná-los usando CIN funções no compilador Visual C ++ ou Symantec.

  • Liste as funções DLL na seção EXPORTS do arquivo de definição de módulo se você estiver usando _stdcall.

  • Liste as funções DLL que outros aplicativos chamam na seção EXPORTS do arquivo de definição de módulo ou para incluir a palavra-chave _declspec (dllexport) na declaração da função.

  • Se você usar um compilador C ++, exporte funções com a instrução extern .C. {} Em seu arquivo de cabeçalho para evitar mutilação de nome.

  • Se você estiver escrevendo sua própria DLL, não deve recompilar uma DLL enquanto a DLL é carregada na memória por outro aplicativo. Antes de recompilar uma DLL, certifique-se de que todos os aplicativos que usam essa DLL específica sejam descarregados da memória. Isso garante que a própria DLL não seja carregada na memória. Você pode falhar ao reconstruir corretamente se esquecer isso e seu compilador não avisar.

  • Teste suas DLLs com outro programa para garantir que a função (e a DLL) se comporte corretamente. Testá-lo com o depurador do seu compilador ou um programa C simples no qual você pode chamar uma função em uma DLL ajudará a identificar se as possíveis dificuldades são inerentes à DLL ou ao LabVIEW.

Vimos como escrever uma DLL e como criar um programa "Hello World". Esse exemplo deve ter dado uma ideia sobre o conceito básico de criação de uma DLL.

Aqui, daremos uma descrição da criação de DLLs usando Delphi, Borland C ++ e novamente VC ++.

Vejamos esses exemplos um por um.

  • Como escrever e chamar DLLs dentro do Delphi

  • Fazendo DLLs a partir do IDE Borland C ++ Builder

  • Fazendo DLLs no Microsoft Visual C ++ 6.0