Desenvolvimento - C#
Criando DLLs no Microsoft.NET
Assim como no Windows, a arquitetura Microsoft .NET permite a criação de DLLs que podem ser compartilhadas por vários programas. O .NET, contudo, traz várias novidades adicionais na criação de DLLs...
por Mauro Sant'AnnaAssim como no Windows, a arquitetura Microsoft .NET permite a criação de DLLs que podem ser compartilhadas por vários programas. O .NET, contudo, traz várias novidades adicionais na criação de DLLs:
- Enquanto as DLLs Windows contém funções, as DLLs .NET contém tipos.
- Dentre os vários tipos possíveis, a class (classe) é o tipo mais interessante que pode existir dentro das DLLs.
- As DLLs contêm não apenas o código, mas também informações de tipo completas do que está dentro dela. Dentre as várias informações, estão os nomes dos tipos, métodos, campos e propriedades.
Esta informação adicional é usada em várias situações, mas a mais importante é garantir a integridade do sistema de tipos em tempo de execução. Não é possível executar uma conversão de tipo inválida, pois todas as conversões são validadas em tempo de execução. Boa parte da segurança e integridade da nova plataforma depende disso. Esta informação de tipo é exposta aos programadores nonamespace “System.Reflections”.
Para quem está acostumado com o Delphi, este é basicamente o mesmo esquema usado nos “runtime packages”, com duas principais diferenças:
- O código executável das DLLs, correspondente no Delphi ao arquivo “BPL”, contém obrigatoriamente informações de tipo, que no Delphi ficam separadas no arquivo “DCP”;
- O Delphi provê informação de tipo em tempo de execução apenas para os campos, métodos e propriedades declarados na seção “published” e, ainda assim, apenas para as classes “registradas”. No .NET, tudo tem informação de tipo em tempo de execução, sem exceções.
A criação de uma DLL contendo classes é bastante simples. No Visual Studio.NET Beta 2, peça a criação de um projeto do tipo “Class Library”. Na verdade, não somos obrigados a colocar classe nenhuma no projeto. O que vale mesmo é que estamos criando uma “Library” (DLL):
Figura 1: Criando uma DLL
Vamos colocar uma classe simples dentro do projeto:
Listagem 1:
using System; namespace MinhaDLL { public class Simples { decimal N; public Simples(decimal X) { AjustaValor(X); } public Simples() { AjustaValor(0); } public void AjustaValor(decimal X) { N = X; } public decimal PegaValor() { return N; } } }
Depois de compilar a DLL, é possível examinar seu conteúdo com o “IL Disassembler” (ildasm.exe), localizado no diretório \Program Files\Microsoft.NET\FrameworkSDK\Bin:
Figura 2: Examinando o conteúdo da DLL
Para testar a DLL, criaremos um projeto em modo console:
Figura 3: Criando projeto Console
Para chamar a classe dentro da DLL, temos duas opções:
- Via “early biding”, resolvendo as chamadas em tempo de compilação.
- Via “late biding”, resolvendo as chamadas em tempo de execução.
Early Biding
Neste caso criaremos uma “referência” à DLL dentro do projeto de teste. Clique com o botão direito sobre “References” e peça “Add Reference”:
Figura 4: Adicionando referência
Selecione a DLL criada recentemente clicando em “Browse”:
Figura 5: Selecionando a DLL para referenciar
Veja o código de teste:
Listagem 2:
using System; namespace ChamaDLL { class Class1 { static void Main(string[] args) { MinhaDLL.Simples MD = new MinhaDLL.Simples(10); decimal X = MD.PegaValor(); Console.WriteLine(X); } } }
No exemplo acima, o executável de testes depende da DLL para ser carregado. Caso a DLL não esteja disponível, o programa nem roda:
Figura 6: Erro por falta da DLL
Podemos resolver a chamada de maneira totalmente dinâmica usando “Late Biding”.
Late Biding
Para chamar a classe via “Late Biding”, usaremos o suporte de “Reflections” do .NET Framework. Copie a DLL para o mesmo diretório do aplicativo e use o seguinte programa em modo console:
Listagem 3:
using System.Reflection; namespace ChamaDLL { class Class1 { static void Main(string[] args) { // Carrega DLL dado seu nome Assembly Lib = Assembly.Load("MinhaDLL"); // Cria um objeto dado seu tipo object MD = Lib.CreateInstance("MinhaDLL.Simples"); // Chama um método MD.GetType().InvokeMember("AjustaValor", BindingFlags.InvokeMethod, null, MD, new object[] {10.0m}); // Chama outro método decimal X = (decimal) MD.GetType().InvokeMember( "PegaValor", BindingFlags.InvokeMethod, null, MD, null); System.Console.WriteLine(X); } } }
Segurança e DLL Hell
A DLL feita neste programa foi copiada no diretório do próprio executável que a usa, sendo também chamada de “private assembly”, pois é acessada apenas dentro de um pacote de software. Este é um caso bastante comum, que não necessita de nenhuma preocupação adicional de segurança ou controle de versão. No entanto, se a DLL for ser compartilhada, existem algumas preocupações adicionais.
No Windows, temos sérios problemas de controle de versões de DLL:
- Um programa de terceiros pode instalar uma versão diferente de uma DLL compartilhada, fazendo o seu programa parar de funcionar.
- Um programa malicioso pode instalar uma DLL diferente, que viole a segurança do sistema.
O .NET tem um protocolo bastante rigoroso para instalação de DLLs compartilhadas, também chamadas de “shared assemblies”, usando criptografia de chave pública. Este protocolo exige os seguintes passos:
- Precisamos “assinar” a DLL usando uma chave criptográfica única da nossa empresa.
- A DLL é instalada com um utilitário específico, chamado “gacutil.exe”.
Conclusão
A arquitetura Microsoft.NET tem um mecanismo bastante poderoso e flexível para a divisão do código em várias DLL, sem perder a “tipagem” forte e aumentando a integridade e segurança.