INFO: Usando _declspec(dllimport) & _declspec(dllexport) no código

Traduções deste artigo Traduções deste artigo
ID do artigo: 132044 - Exibir os produtos aos quais esse artigo se aplica.
Expandir tudo | Recolher tudo

Neste artigo

Sumário

Este artigo complementa as informações abordadas no seguinte artigo na Base de dados de Conhecimento da Microsoft:
107501INFO: __export substituído por __declspec no Visual C++ de 32 bits
Este artigo descreve as vantagens e mecânica do uso _declspec(dllimport) e _declspec(dllexport) em seu aplicativo.

Mais Informações

A edição de 32 bits do Visual C++ usa _declspec(dllimport) e _declspec(dllexport) para substituir a palavra-chave __export anteriormente usada em versões de 16 bits do Visual C++.

Você não precisa usar _declspec(dllimport) para seu código para compilar corretamente, mas ao fazer isso assim permite que o compilador gere um código melhor. O compilador é capaz de gerar um código melhor porque ele sabe com certeza se uma função existe em uma DLL ou não, portanto, o compilador pode produzir códigos ignorar um nível de indireção que normalmente estar presente em uma chamada de função ultrapassou um limite DLL.

Com a adequado .def arquivo EXPORTS seção _declspec(dllexport) não é necessária. _declspec(dllexport) foi adicionado para proporcionar uma maneira fácil de exportar funções de um .exe ou .dll sem usar um arquivo .DEF.

O restante deste artigo fornece uma discussão bastante baixo nível, completa sobre esses problemas.

O formato de executável portátil do Win32 é projetado para minimizar o número de páginas que devem ser utilizadas para corrigir importações. Para fazer isso, ele coloca todos os endereços importação para qualquer programa em um local chamado tabela de endereços de importar. Isso permite que o carregador modificar apenas uma ou duas páginas ao acessar esses importações.

Usando _declspec(dllimport) para chamadas de função

No exemplo de código a seguir, presuma func1 é uma função que reside em uma DLL separada do arquivo .exe que contém a função main().

Sem _declspec(dllimport), dado esse código:
void main(void) {
    func1();
}
				
o compilador gera código parecido com este:
call func1
				
e o vinculador converte a chamada em algo assim:
call 0x4000000         ; The address of 'func1'.
				
se 'func1' existe na DLL de outro, o vinculador não pode resolver isso diretamente porque ela tem nenhuma maneira de saber o que é o endereço do 'func1'. Em ambientes de 16 bits, o vinculador adiciona este endereço de código a uma lista no .exe de que o carregador seria patch em tempo de execução com o endereço correto. Em ambientes de 32 bits, o vinculador gera uma conversão para o qual ele sabe o endereço. A conversão tenha esta aparência:
   0x40000000:    jmp DWORD PTR __imp_func1
				
aqui __imp_func1 é o endereço para slot do func1 na tabela de endereço a importação do arquivo .exe. Todos os endereços são conhecidos, portanto, para o vinculador. O carregador tem apenas atualizar a tabela de importação de endereços do arquivo .exe no tempo de carregamento para que tudo funcione corretamente.

Portanto, usar _declspec(dllimport) é melhor porque ele é melhor se o vinculador não gera uma conversão se não tiver a. Thunks tornar o código maior (nos sistemas RISC, ele pode ser várias instruções) e pode degradar o desempenho do cache. Se você solicitar o compilador que a função esteja em uma DLL, ele pode gerar uma chamada indireta para você.

Agora este código:
__declspec(dllimport) void func1(void);

void main(void) {
    func1();
}
				
gera essa instrução:
call DWORD PTR __imp_func1
				
há nenhuma conversão e instruções não jmp, portanto, o código é menor e mais rápido.

Por outro lado, para chamadas de função dentro de uma DLL, você não quer ter usar uma chamada indireta. Você já sabe o endereço da função. Tempo e espaço são necessários para carregar e armazenar o endereço da função antes de uma chamada indireta, portanto, uma chamada direta é sempre mais rápido e menor. Você deseja somente usar __declspec(dllimport) ao chamar funções DLL de fora a DLL propriamente dito. Não use __declspec(dllimport) funções dentro de uma DLL ao criar essa DLL.

Usando _declspec(dllexport)

A Microsoft introduziu __export na versão de 16 bits compilador para permitir que o compilador gerar automaticamente os nomes de exportação e colocá-los em um arquivo .lib. Este arquivo .lib, em seguida, poderia ser usado como um .lib estático para vincular com uma DLL.

A Microsoft adicionou __declspec(dllexport) para continuar essa conveniência. Sua finalidade é adicionar a diretiva de exportação para o arquivo de objeto, portanto, você não precisará de um arquivo .DEF.

Essa conveniência é mais evidente quando tentar exportar decorada nomes de função do C++. Não há nenhuma especificação padrão para decoração de nome, para que o nome de uma função exportada pode alterar entre versões do compilador. Se você usar _declspec(dllexport), recompilar o DLL e os arquivos .exe dependentes é necessário somente à conta para alterações de convenção de nomeação.

Muitos exportar diretivas como ordinais, NONAME ou PRIVATE, pode ser feita somente em um arquivo .def e não há nenhuma maneira para especificar esses atributos sem um arquivo .DEF. No entanto, usar _declspec(dllexport) ao usar um arquivo .def não causa erros de compilação.

Como uma referência, pesquise o arquivo de cabeçalho WINBASE.H Win32. Ele contém exemplos de uso __declspec(dllexport) e __declspec(dllimport) preferencial.

Usando _declspec(dllexport) e _declspec(dllimport) em dados

No caso de dados, usar _declspec(dllimport) é um item de conveniência que remove uma camada de indireção. Quando você importa dados de uma DLL, você ainda precisa percorrer a tabela de importação de endereços. Nos dias Win32 antes _declspec(dllimport), isso significava que você tinha que lembre-se de fazer um nível extra de indireção quando acessar dados exportado da DLL:
// project.h
#ifdef _DLL     // If accessing the data from inside the DLL
   ULONG ulDataInDll;

else            // If accessing the data from outside the DLL
   ULONG *ulDataInDll;
#endif
				
, em seguida, você deve exportar os dados em seu arquivo .def:
// project.def
LIBRARY project
EXPORTS
    ulDataInDll   CONSTANT
				
e acessá-lo fora a DLL:
if (*ulDataInDll == 0L) {
   // Do stuff here
}
				
quando você marca os dados como __declspec(dllimport), o compilador gera automaticamente o código de indireção para você. Você não precisa se preocupar sobre as etapas acima. Conforme mencionado anteriormente, não use _declspec(dllimport) declaração nos dados ao criar a DLL. Funções dentro da DLL não usará a tabela de endereços de importação para acessar o objeto de dados. Portanto, você não tenha o nível extra de indireção presente.

Para exportar os dados automaticamente da DLL, use essa declaração:
__declspec(dllexport) ULONG ulDataInDLL;
				

Usando um arquivo .def

Se você optar por usar __declspec(dllimport) juntamente com um arquivo .def, você deve alterar o arquivo .def para usar dados no lugar da constante para reduzir a probabilidade de que a codificação incorreta causará um problema:
// project.def
LIBRARY project
EXPORTS
    ulDataInDll   DATA
				
o gráfico a seguir mostra por que:
Keyword     Emits in the import lib     Exports
CONSTANT    __imp_ulDataInDll           ulDataInDll
            __ulDataInDll

DATA        __imp_ulDataInDll           ulDataInDll
				
usando _declspec (dllimport) e constante lista a versão __imp_ e o nome não decorado na biblioteca de importação .lib DLL que é criado para permitir a vinculação explícita. Usando _declspec(dllimport) e dados lista apenas a versão __imp_ do nome.

Se você usar a constante, uma das seguintes construções de código poderia ser usada para acessar o ulDataInDll:
__declspec(dllimport) ULONG ulDataInDll; /*prototype*/ 
   if (ulDataInDll == 0L)   /*sample code fragment*/ 
				
- ou -
ULONG *ulDataInDll;      /*prototype*/ 
if (*ulDataInDll == 0L)  /*sample code fragment*/ 
				
No entanto, se você usar dados em seu arquivo .def, somente código compilado com a seguinte definição pode acessar a variável ulDataInDll:
__declspec(dllimport) ULONG ulDataInDll;
if (ulDataInDll == 0L)   /*sample code fragment*/ 
				
usando constante é mais arriscado porque se você esquecer de usar o nível extra de indireção, você poderia acessar potencialmente ponteiro a importar tabela de endereços para a variável--não a variável. Esse tipo de problema freqüentemente pode se manifestar como uma violação de acesso porque a tabela de endereços de importação no momento é feita somente leitura pelo compilador da Microsoft e vinculadores.

O Vinculador Visual C++ atual emite um aviso se vir constante no arquivo .def para conta para esse caso. O motivo real somente para usar a constante é se você não recompilar algum arquivo do objeto onde o arquivo de cabeçalho não lista dllimport no protótipo.

Referências

O Visual C++ Books Online fornecer uma quantidade considerável de documentação sobre o dllexport e dllimport atributos de classe de armazenamento. Isso inclui "Os dllexport e dllimport atributos" e os tópicos "usando dllimport e dllexport em C++" no capítulo "Microsoft específicos modificadores" de referência de linguagem C++ e os tópicos "Exportando símbolos" no capítulo "Criando DLLs para Win32" da referência técnicas de programação. Para obter uma listagem completa tópicos relacionados, procure os manuais online para "dllimport" ou "dllexport".

Para obter mais informações, leia os seguintes artigos na Base de dados de Conhecimento da Microsoft:
90530Como exportar dados de uma DLL ou um aplicativo
107501INFO: __export substituído por __declspec no Visual C++ de 32 bits

Propriedades

ID do artigo: 132044 - Última revisão: terça-feira, 2 de dezembro de 2003 - Revisão: 2.0
A informação contida neste artigo aplica-se a:
  • Microsoft Visual C++ 1.0 Professional Edition
  • Microsoft Visual C++ 2.0 Professional Edition
  • Microsoft Visual C++ 2.1
  • Microsoft Visual C++ 4.0 Standard Edition
  • Microsoft Visual C++ 5.0 Enterprise Edition
  • Microsoft Visual C++ 5.0 Professional Edition
Palavras-chave: 
kbmt kbcode kbcompiler kbinfo KB132044 KbMtpt
Tradução automática
IMPORTANTE: Este artigo foi traduzido por um sistema de tradução automática (também designado por Machine Translation ou MT), não tendo sido portanto traduzido ou revisto por pessoas. A Microsoft possui artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais, com o objetivo de oferecer em português a totalidade dos artigos existentes na base de dados de suporte. No entanto, a tradução automática não é sempre perfeita, podendo conter erros de vocabulário, sintaxe ou gramática. A Microsoft não é responsável por incoerências, erros ou prejuízos ocorridos em decorrência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza atualizações freqüentes ao software de tradução automática (MT). Obrigado.
Clique aqui para ver a versão em Inglês deste artigo: 132044
Aviso de Isenção de Responsabilidade sobre Conteúdo do KB Aposentado
Este artigo trata de produtos para os quais a Microsoft não mais oferece suporte. Por esta razão, este artigo é oferecido "como está" e não será mais atualizado.

Submeter comentários

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com