Para gerar um executável pequeno, uma das práticas mais utilizadas é a de modularizar o projeto em vários EXEs, DLLs, BPLs, e manter separados os arquivos auxiliares de texto, imagens ou sons. Essa prática é útil para diminuir o tráfego de arquivos durante as atualizações do sistema, executando o update apenas daqueles que realmente sofreram modificações.

No entanto, caso seja necessário manter um único arquivo para todo o sistema, o Delphi oferece a possibilidade de trabalhar com resources, uma forma de embutir arquivos externos dentro do executável e acessá-los posteriormente quando necessário.

Nesse artigo veremos como utilizar essa funcionalidade a partir do Delphi 2010, versão na qual o IDE passou a oferecer uma interface amigável para gerenciar os recursos.

Adicionando arquivos ao projeto

Embora esse recurso exista desde a versão 3 do Delphi, a forma de incorporar arquivos externos ao executável foi melhorada significativamente a partir da versão 2009. Atualmente, é possível utilizar um gerenciador de recursos que pode ser acessado através do menu Project > Resources and Images. Na Figura 1 é possível observar diversos arquivos adicionados através da tela de gerenciador de recursos.

Tela de gerenciamento de Resources
Figura 1. Tela de gerenciamento de Resources

Assim que os arquivos são adicionados, eles aparecem no Projetct Manager do Delphi, onde você poderá selecioná-los e alterar suas propriedades no Object Inspector conforme mostra a Figura 2.

Project Manager com arquivos adicionados aos recursos
Figura 2. Project Manager com arquivos adicionados aos recursos

Uma vez que os arquivos tenham sido adicionados, quando o projeto é compilado, é gerado um arquivo de texto com extensão *.rc, que contém em cada linha um “alias” (apelido) para referenciar o arquivo, seu caminho físico e o tipo de dado do recurso. O conteúdo desse arquivo referente à Figura 2 pode ser visto na Listagem 1.

Listagem 1. Conteúdo do arquivo ProjectResourcesTestResource.rc

      WAVRelogio RCDATA "8672.mp3"
      DllAuxiliar RCDATA "auxiliar.dll"
      ExeCalc RCDATA "calc.exe"
      IMGDevmediaLogo RCDATA "devmedia2peq.PNG"
      TXTuf RCDATA "Siglas-UF.txt"
      IMGPredio BITMAP "Company-icon.bmp"
      

Depois do arquivo *.rc ter sido gerado, o Delphi o compila em uma versão intermediária com extensão *.dres, que será utilizada durante o processo de linkagem e posteriormente gerará um novo arquivo de extensão *.res, que finalmente é encapsulado dentro do executável.

Reutilizando arquivos de texto

Os arquivos que não se encaixam nos padrões pré-definidos para resources (bitmaps, ícones e arquivos de fontes), como os de texto, são encapsulados como um tipo binário denominado RCData. A partir disso, para acessá-los, é necessário fazer uso de uma classe denominada TResourceStream, que permite carregar o arquivo em qualquer componente que tenha uma propriedade do tipo TStrings, utilizando para isso uma única linha de código. Por exemplo, podemos carregar facilmente o conteúdo do arquivo e exibi-lo em um ListBox com o seguinte comando:

ListBox1.Items.LoadFromStream(TResourceStream.Create(HInstance,"TXTuf",RT_RCDATA));

Ao instanciar a classe TResourceStream na chamada do método LoadFromStream da classe TStrings, por exemplo, é necessário passar ao menos três parâmetros: uma variável de ponteiro chamada HInstance, o “alias” que faz referência ao recurso e uma constante que representa o tipo de dado no qual o recurso está armazenado no executável.

Reutilizando arquivos de imagens

Os arquivos de imagem podem ser reutilizados de duas formas, dependendo do seu tipo. Caso sejam imagens de ícones, arquivos *.bmp e cursores, existe o método LoadFromResourceName, que permite carregá-los com apenas uma linha de código:

Image2.Picture.Bitmap.LoadFromResourceName(HInstance,"IMGPredio");

Observe que por se tratar de uma imagem com extensão *.bmp, o método não contém rotinas de conversão adicionais, algo que não ocorre com imagens de outros formatos, que são compiladas como binários.

A segunda forma é utilizada quando tratamos de imagens do tipo PNG ou JPEG, por exemplo, casos em que é preciso utilizar classes que tratem adequadamente o recurso. Podemos ver um exemplo disso na Listagem 2.

Listagem 2. Exemplo de reutilização de um uma imagem *.png como resource

procedure TForm3.Button2Click(Sender: TObject);
var
  png: TPngImage;
begin
  png:=TPngImage.Create;
  png.LoadFromResourceName(HInstance, "IMGDevmediaLogo");
  Image1.Picture.Graphic:=png;
end; 
    
  • Linha 05: Instanciamos um objeto do tipo TPngImage;
  • Linha 06: O método LoadFromResourceName recebe o “alias” da imagem do tipo *.png que foi compilada como RCData;
  • Linha 07: Atribuímos a instância png para a propriedade Graphic, que resulta na exibição da imagem no componente.

Reutilizando arquivos de áudio, executáveis, DLLs, entre outros

Os arquivos de tipos diferentes dos que vimos até agora também são compilados como RCData, mas para utilizá-los é preciso extraí-los utilizando a combinação das classes TResourceStream e TFileStream, conforme apresentado na Listagem 3. Nesse exemplo fizemos uso de um arquivo MP3, mas o mesmo código serviria para os demais tipos, como executáveis ou DLLs.

Listagem 3. Exemplo de reutilização de um arquivo MP3 como resource

procedure TForm3.btnExecutarMP3(Sender: TObject);
var
  fs: TFileStream;
  rs: TResourceStream;
  s : string;
  m : TMediaPlayer;
begin
  rs := TResourceStream.Create(hInstance, "WAVRelogio", RT_RCDATA);
  s  := ExtractFilePath(Application.ExeName)+"file.mp3";
  fs := TFileStream.Create(s,fmCreate);
  rs.SaveToStream(fs);
  fs.Free;

  MediaPlayer.Close;
  MediaPlayer.FileName:=s;
  MediaPlayer.Open;
  MediaPlayer.Play;
end;
    
  • Linha 08: Instanciamos um TResourceStream, passando como parâmetro o “alias” do arquivo MP3 e o tipo de dado (RCData);
  • Linha 09: Atribuímos à variável string o caminho de um diretório para extrair o arquivo;
  • Linhas 10 e 11: Instanciamos a classe TFileStream, passando o caminho onde o arquivo será criado. Na sequência, salvamos todo o conteúdo do resource carregado na linha 8 no arquivo; Linhas 14 a 17: Executamos o arquivo MP3 extraído do executável.

Embora sejam muito úteis para transportar arquivos no próprio executável e reutilizá-los dentro do sistema, deve-se ter cuidado com o uso de resources. Como não há uma compactação efetiva do arquivo, seu tamanho normalmente acaba aumentando mais do que o esperado.