Ir para o conteúdo

Dynamic-Link Libraries

Breve introdução

Dynamic-link library, ou DLL como são mais conhecidas, são bibliotecas partilháveis, transportáveis, que permitem a utilização de determinada lógica ou método de forma dinâmica e reutilizável. Desta forma é possível simplificar aplicações complexas, sem ser necessário reescrever código para algumas operações comuns e comprovadas. Da mesma forma que se conseguem escrever blocos comuns, também é possível escrever implementações específicas que funcionariam numa aplicação como módulos que estenderiam a sua funcionalidade.

Uma abordagem diferente

Imaginemos a nossa aplicação como um ponto de convergência onde se monta um determinado produto a partir de outros produtos. Cada fábrica periférica utiliza o seu próprio método de fabrico, dependendo do tipo de produto que estão a fabricar, mas independentemente da forma como são fabricados, o produto final chega da mesma forma ao ponto de convergência.

Imaginemos agora que com o passar do tempo, a modernização da indústria levou à completa alteração dos processos de fabrico de algumas fábricas, melhorando os passos necessários o material utilizado e o consumo de energia. Estas fábricas são remodeladas, mas o seu produto continua a ser exactamente o mesmo. Cada fábrica independente é como uma DLL para a aplicação. São meros pontos de chamada cujo único interesse que a aplicação tem nelas é localizá-las, enviar um pedido e receber uma resposta. Não interessa o que lá foi feito para a obter. É indiferente para a aplicação.

Quando usar?

As DLL são tipicamente utilizadas para cobrir uma determinada funcionalidade completamente fora do âmbito da aplicação, como por exemplo as bibliotecas DirectX que servem de proxy com o hardware de uma tal forma que permite a quem desenvolve centrar o foco naquilo que realmente é o seu negócio. Quem desenvolve video-jogos, por exemplo, teria de outra forma que escrever os métodos de utilização dos recursos da máquina em cada jogo que produzisse, o que não faz qualquer sentido. Como já referido, as DLL podem também ser utilizadas como módulos de expansão a uma aplicação.

Uma DLL escrita para .Net está habilitada a ser interpretada por qualquer idioma .Net. Com isto, é possível distribuír blocos independentes, implementados em qualquer idioma .Net para serem utilizados noutro, de forma completamente transparente. São estas as DLL a que o artigo se refere.

A minha primeira DLL

Nada como um exemplo simples para exemplificar o que se tenta explicar. Vamos cobrir todo o processo desde a criação até à utilização, passando pela referência. O objectivo principal é descentralizar um pequeno algoritmo que aclara ou escurece imagens. Vamos chamar à biblioteca Luminus, apenas para fácil referência.

Ideia: Os passos foram executados para VB.Net utilizando o IDE 2010 Express.

Passo 1

Criar um novo projecto Class Library e dar-lhe o nome Luminus.

Passo 2

Escrever a implementação. A implementação terá de adoptar uma posição independente. Lembremo-nos que não existe forma implícita de fazer referências às instâncias da aplicação, e mais importante que tudo, lembremo-nos que esta DLL pode ser implementada em "n" aplicações completamente diferentes umas das outras. Deverá ser escrita de uma forma que produza respostas genéricas ou de fácil interpretação. No caso de DLL para .Net, é nos possível transportar objectos nativos, quer sejam primitivos ou não, o que nos facilita bastante a lógica e reduz a possibilidade das respostas serem mal interpretadas. Para o artigo, e para o nosso Luminus, vamos precisar apenas de uma classe que contém dois métodos estáticos, ou seja, esta biblioteca não precisa de ser instânciada.

Imports System.Drawing

Public Class Imagem

    Public Shared Sub Aclarar(ByRef Imagem As bitmap, ByVal Intensidade As Integer)
        For X As Integer = 0 To Imagem.Width - 1
            For Y As Integer = 0 To Imagem.Height - 1
                Dim CorFinal As Color = Imagem.GetPixel(X, Y)
                CorFinal = Color.FromArgb(Limitar(CorFinal.R + Intensidade), _
                                          Limitar(CorFinal.G + Intensidade), _
                                          Limitar(CorFinal.B + Intensidade))
                Imagem.SetPixel(X, Y, CorFinal)
            Next
        Next
    End Sub

    Public Shared Sub Escurecer(ByRef Imagem As bitmap, ByVal Intensidade As Integer)
        For X As Integer = 0 To Imagem.Width - 1
            For Y As Integer = 0 To Imagem.Height - 1
                Dim CorFinal As Color = Imagem.GetPixel(X, Y)
                CorFinal = Color.FromArgb(Limitar(CorFinal.R - Intensidade), _
                                          Limitar(CorFinal.G - Intensidade), _
                                          Limitar(CorFinal.B - Intensidade))
                Imagem.SetPixel(X, Y, CorFinal)
            Next
        Next
    End Sub

    Private Shared Function Limitar(ByVal Valor As Integer) As Integer
        If Valor > 255 Then Return 255
        If Valor < 0 Then Return 0
        Return Valor
    End Function
End Class

Se repararem, ao transcreverem o bloco de código, não é possível fazer o Imports do namespace Drawing. Ao escrever uma DLL, é assumido que as dependências .Net estejam reduzidas ao mínimo possível, e esse mínimo não abrange, por exemplo, o namespace Drawing. Para resolver a situação basta acrescentar uma referência a esta biblioteca, na nossa biblioteca.

Passo 2A

Para acrescentar uma referência .Net, devemos aceder às propriedades do nosso projecto. A partir daí, seleccionamos o separador referente às referências (References). São listadas as referências que existem actualmente, e Drawing não faz parte delas. Para a adicionar, carregar em adicionar (Add). Na janela apresentada, seleccionamos o separador .Net, localizamos e adicionamos a biblioteca System.Drawing. Com este passo concluído passa a ser possível o import deste namespace.

Passo 3

Para que a nossa biblioteca fique pronta para ser utilizada, temos de a compilar (Build). Uma "Class Library" não pode ser alvo de um "Run" comum (F5) pois não possuí nada, nem concreto, nem genérico o suficiente para que possa existir um ponto de teste comum, e por esta razão o mais adequado é criar um projecto de teste. Podemos criar esse projecto numa solução diferente, mas aconselho a criar na própria solução por ser mais fácil e fazer mais sentido. Também podemos optar por não testar a biblioteca. Nesse caso basta efectuar um "Build" e é gerado na pasta de release um ficheiro DLL.

Passo 3A

Para adicionar um projecto à solução e referenciar a biblioteca Luminus, teremos primeiro de adicionar o projecto propriamente dito. Para tal basta aceder a File (Ficheiro) --> Add (Adicionar) --> New Project (Novo Projecto) e seleccionar Windows Form, WPF ou Console Application. No explorador da solução (Solution Explorer), é necessário marcar este novo projecto de teste como projecto de arranque, "Set as StartUp Project" a partir do menu de contexto. Para facilitar o processo de "Build" encadeado (isto porque as alterações na biblioteca só se reflectem na aplicação caso esta esteja compilada), seleccionamos as dependências do projecto (Project Dependencies) a partir do menu de contexto do projecto de teste e seleccionar Luminus como dependência deste projecto. Isto fará com que cada execução do projecto de teste, a biblioteca Luminus seja recompilada.

Passo 3B

Mais importante do que este último ponto é referenciar a Luminus no projecto de teste. Seleccionamos "Adicionar referência" (Add reference) do menu de contexto do projecto de teste, e escolhemos o projecto Luminus a partir do separador "Project", onde são listados os restantes projectos da solução.

Neste momento temos a biblioteca Luminus correctamente referenciada no projecto de teste, e podemos começar a utilizar. Como os métodos são estáticos, é possível aceder-lhes apenas por

Luminus.Imagem.Aclarar ou Luminus.Imagem.Escurecer

Ideia: Também é possível instanciar classes de uma biblioteca, da mesma forma que se instanciam classes de qualquer namespace nativo.

Depois de correctamente testada e compilada, a biblioteca toma forma de um ficheiro DLL que pode ser transportado para outro qualquer projecto, bastando agora referenciar a biblioteca.

Passo 4

Para referenciar a nossa biblioteca Luminus em qualquer outro projecto, basta adicionar a referência. Para isto, basta correr o Passo 3B, seleccionando não o separador "Project", mas o separador "Browse" e localizar o ficheiro Luminus.dll.

Proposta de aplicação

Com base na Luminus, uma aplicação que aclare e escureca imagens pode ficar tão simplificada como dois botões:

Public Class FormPrincipal
    Private Enum TipoOper
        Aclarar
        Escurecer
    End Enum

    Private Sub btAclarar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btAclarar.Click
        Operacao(TipoOper.Aclarar)
    End Sub

    Private Sub btEscurecer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btEscurecer.Click
        Operacao(TipoOper.Escurecer)
    End Sub

    Private Sub Operacao(ByVal Tipo As TipoOper)
        Dim OFD As New OpenFileDialog
        OFD.Filter = "Imagens BMP|*.bmp"
        OFD.ShowDialog()

        Dim B As New Bitmap(OFD.FileName)

        Select Case Tipo
            Case TipoOper.Aclarar : Luminus.Imagem.Aclarar(B, 80)
            Case TipoOper.Escurecer : Luminus.Imagem.Escurecer(B, 80)
        End Select

        Dim SFD As New SaveFileDialog
        SFD.Filter = "Imagens BMP|*.bmp"
        SFD.ShowDialog()

        B.Save(SFD.FileName)
    End Sub
End Class

Ir mais além

É impossível fazer justiça às possibilidades das DLL neste simples artigo e existem tantas utilidades quantas a imaginação de cada um possa alcançar.