Desenvolver DLLs

Aplica-se a: Excel 2013 | Office 2013 | Visual Studio

Uma biblioteca é de uma base de códigos compilados que fornece algumas funcionalidades e dados para um aplicativo executável. As bibliotecas podem ser vinculadas estaticamente ou vinculadas dinamicamente e, convencionalmente, têm extensões de nome de arquivo .lib e .dll, respectivamente. Bibliotecas estáticas (como a biblioteca de tempo de execução C) são vinculadas ao aplicativo na compilação e, portanto, tornam-se parte da resultante executável. O aplicativo carrega uma DLL quando necessário, normalmente quando o aplicativo é iniciado. Um DLL pode carregar e vincular dinamicamente a DLL do outro.

Benefícios do uso de DLLs

Os principais benefícios de DLLs são os seguintes:

  • Todos os aplicativos podem compartilhar uma única cópia no disco.
  • Os arquivos executáveis dos aplicativos são menores.
  • Eles permitem que grandes projetos de desenvolvimento sejam desmembrados. Desenvolvedores de aplicativo e DLL só precisam concordar uma interface entre suas respectivas partes. Essa interface é exportada pela DLL.
  • Os desenvolvedores de DLL podem atualizar as DLLs, talvez para torná-las mais eficientes ou para corrigir um bug, sem precisar atualizar todos os aplicativos que as utilizam, desde que a interface exportada da DLL não seja alterada.

Você pode usar DLLs para adicionar comandos e funções de planilha no Microsoft Excel.

Recursos para a criação de DLLs

Para criar uma DLL, você precisa de:

  • Um editor de código-fonte.
  • Compilador para ativar o código-fonte do código de objeto que seja compatível com o seu hardware.
  • Vinculador para adicionar o código de bibliotecas estáticas, quando usado e para criar o arquivo executável DLL.

Ambientes de desenvolvimento integrados modernos (IDEs), como o Microsoft Visual Studio, fornecem todas essas coisas. Eles também fornecem muito mais: editores inteligentes, ferramentas para depurar seu código, ferramentas para gerenciar vários projetos, novos assistentes de projeto e muitas outras ferramentas importantes.

Você pode criar DLLs em vários idiomas, por exemplo, C++ Pascal e Visual Basic. Dado que o código-fonte da API fornecido com o Excel é C e C ++, somente esses dois idiomas são considerados nesta documentação.

Exportar comandos e funções

Ao compilar um projeto DLL, o compilador e o vinculador precisam saber quais funções devem ser exportadas para que possam disponibilizá-las para o aplicativo. Esta seção descreve as maneiras de fazer isso.

Quando compiladores compilam o código-fonte, em geral, eles alteram os nomes das funções de sua aparência no código-fonte. Eles geralmente fazem isso adicionando o início e/ou final do nome de um processo denominado nome decoração. Você precisa garantir que a função é exportada com um nome reconhecível para o aplicativo ao carregar a DLL. Isso pode significar informar ao vinculador para associar o nome decorado com um nome de exportação mais simples. O nome da exportação pode ser o nome que apareceu originalmente no código-fonte ou qualquer outra coisa.

A maneira como o nome é decorado depende do idioma e de como o compilador é instruído a disponibilizar a função, ou seja, a convenção de chamada. A convenção de chamada padrão entre processos para o Windows usada por DLLs é conhecida como a convenção WinAPI. Ele é definida nos arquivos de cabeçalho do Windows como WINAPI, que por sua vez é definido usando o __stdcall do declarador do Win32.

Uma função de exportação de DLL para uso com o Excel (se é uma função de planilha, função equivalente de folha de macro ou comando definido pelo usuário) sempre deve usar a convenção de chamadaWINAPI / __stdcall. É necessário incluir o especificador WINAPI explicitamente na definição da função, pois o padrão nos compiladores Win32 é usar a convenção de chamada __cdecl, também definida como WINAPIV, se nenhuma outra for especificada.

Você pode dizer ao vinculador que uma função deve ser exportada, e o nome deve ser conhecido externamente de uma das várias maneiras:

  • Coloque a função em um arquivo DEF após a palavra-chave EXPORTS e defina a configuração do projeto DLL para fazer referência a esse arquivo, vincular.
  • Use o declarador __declspec(dllexport) na definição de função.
  • Use uma diretiva de pré-processador #pragma diretiva para enviar o vinculador.

Embora seu projeto possa usar todos os três métodos e seu compilador e vinculador os suportem, você não deve tentar exportar uma função em mais de uma destas maneiras. Por exemplo, suponha que uma DLL contenha dois módulos de código-fonte, um C e um C++, que contêm duas funções a serem exportadas, my_C_export e my_Cpp_export respectivamente. Para simplificar, suponha que cada função receba um único argumento numérico de precisão dupla e retorne o mesmo tipo de dados. As alternativas para exportar cada função usando cada um desses métodos são descritas nas seções a seguir.

Usando um arquivo DEF

double WINAPI my_C_export(double x)
{
/* Modify x and return it. */
    return x * 2.0;
}
double WINAPI my_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

O arquivo DEF precisa conter essas linhas.

EXPORTS my_C_export = _my_C_export@8 my_Cpp_export

A sintaxe geral de uma linha que segue uma instrução EXPORTS é a seguinte.

entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]

Observe que a função C foi decorada, mas o arquivo DEF explicitamente força o vinculador a expor a função usando o nome do código-fonte original (neste exemplo). O vinculador exporta implicitamente a função C ++ usando o nome de código original, para que não seja necessário incluir o nome decorado no arquivo DEF.

Para chamadas de função de API do Windows de 32 bits, a convenção para a decoração de funções compiladas em C é a seguinte: function_name se torna function_name@n em que n é o número de bytes expressos como um decimal tomado por todos os argumentos, com os bytes para cada arredondado até o múltiplo mais próximo de quatro.

Observação

Todos os ponteiros são quatro bytes de largura no Win32. O tipo de retorno não terá impacto sobre o nome da decoração.

É possível forçar o compilador C ++ a expor nomes não-definidos para funções C ++ colocando a função e quaisquer protótipos de função dentro de um "C" externo {…} bloqueio, como é mostrado neste exemplo. (As chaves {} foram omitidos aqui porque a declaração refere-se somente ao bloco de código de função que o segue imediatamente).

extern "C"
double WINAPI my_undecorated_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

Quando você está colocando protótipos de função C em arquivos de cabeçalho que podem ser incluídos em arquivos de origem C ou C ++, inclua a seguinte diretiva de pré-processador.

#ifdef __cplusplus
extern "C" {
#endif
double WINAPI my_C_export(double x);
double WINAPI my_Cdecorated_Cpp_export(double x);
#ifdef __cplusplus
}
#endif

Usando declarador __declspec(dllexport)

A palavra-chave __declspec(dllexport) pode ser usada na declaração de função da seguinte maneira.

__declspec(dllexport) double WINAPI my_C_export(double x)
{
/* Modify x and return it. */
    return x * 2.0;
}

A palavra-chave __declspec(dllexport) deve ser adicionada à esquerda da declaração. As vantagens dessa abordagem são que a função não precisa ser listada em um arquivo DEF e que o status de exportação está correto com a definição.

Se você quiser evitar que uma função C ++ seja disponibilizada com a decoração do nome C ++, você deve declarar a função da seguinte maneira.

extern "C"
__declspec(dllexport) double WINAPI my_undecorated_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

O vinculador disponibilizará a função como my_undecorated_Cpp_export, ou seja, o nome como aparece no código-fonte sem decoração.

Usando uma diretiva de vinculador de pré-processador #pragma

Versões recentes do Microsoft Visual Studio suportam duas macros predefinidas que, quando usadas em conjunto com uma diretiva#pragma, permite que você instrua o vinculador a exportar a função diretamente de dentro do código de função. As macros são FUNCTION e FUNCDNAME (observe o sublinhado duplo em cada extremidade) que são expandidas para os nomes das funções não decoradas e decoradas, respectivamente.

Por exemplo, quando você está usando o Microsoft Visual Studio, essas linhas podem ser incorporadas em um arquivo de cabeçalho comum da seguinte maneira.

#if _MSC_VER > 1200 // Later than Visual Studio 6.0
#define EXPORT comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
#else // Cannot use this way of exporting functions.
#define EXPORT
#endif // else need to use DEF file or __declspec(dllexport)

Se esse cabeçalho estiver incluído nos arquivos de origem, as duas funções de exemplo poderão ser exportadas da seguinte maneira.

Código C:

double WINAPI my_C_export(double x)
{
#pragma EXPORT
/* Modify x and return it. */
    return x * 2.0;
}

Código C++:

double WINAPI my_Cpp_export(double x)
{
#pragma EXPORT
// Modify x and return it.
    return x * 2.0;
}

Observe que a diretiva deve ser colocada no corpo da função e só está expandida quando nenhuma das opções do compilador /EP ou /p é definida. Essa técnica elimina a necessidade de um arquivo DEF ou declaração __declspec(dllexport)e mantém a especificação do seu status de exportação com o código de função.

Confira também