Using resources in FPC/Lazarus

Há muita diferença entre os arquivos resources no Delphi e no Lazarus.

Resources é qualquer arquivo que você deseje embutir junto com o executável. A prática comum é ter imagens, efeitos sonoros, arquivos html/dll/etc espalhados ou distribuídos no disco durante a instalação. Usar resources significa que você pode embuti-lo no executável e ao executá-lo então usufruir desses recursos da forma que desejar. Por exemplo, a utilização do CEF4(Navegador Chromium) embutido em nossa aplicação irá requerer algumas DLLs no mesmo diretório do executável, claro eu posso criar um instalador que distribua essas DLLs para minha aplicação ou então usar o resources para transferir essas DLLs de dentro do executável para a pasta de instalação e garantir que o programa funcione com as DLLs planejadas. Mas seu uso não é restrito para extrairmos arquivos para o disco, essa é apenas uma das possibilidades, a mais comum é carregar imagens ou tocar efeitos sonoros com arquivos .jpg e .wav sem precisar extrair nada.

A forma como o Lazarus lida com resources – na minha opinião – é muito melhor do que a forma como o Delphi lidai, vamos as diferenças:

No Delphi, no início do projeto ou form você faria assim:

{$R arquivo.res}

No Lazarus usaremos assim:

{$R arquivo.lrs} ou {$I arquivo.lrs}

Não é apenas a extensão ou diretiva que muda, a forma de gerar o arquivo arquivo.lrs também será diferente. Enquanto no Delphi você precisa criar um arquivo intermediário(.rc) contendo a lista dos arquivos que mais tarde serão transferido para um .res, por exemplo, um arquivo.rc assim:

MYDATA RCDATA "mydata.dat"

Onde cada linha deste arquivo(.rc) indicando um arquivo diferente para mais tarde gerar um arquivo.res. O delphi usa um utilitário interno – disponível na IDE – para converter este arquivo .rc->.res, no Lazarus faremos isso através de um programa de linha de comando sem precisar dum arquivo intermediário(.rc), basta:

C:\Lazarus\Tools\lazres.exe arquivo.lrs image1.jpg image2.jpg minhaDLL.dll

E será gerado o arquivo arquivo.lrs que diferentemente do Delphi não é binário e pode ser versionado por ferramentas como o git. Existe também uma GUI que simplifica essa operação e pode ser encontrado neste link:

LRS Explorer

A forma de usar resources que achei mais adequado é incluir na seção Uses:

uses …LResources…;

E para usufruir, vamos evitar fazer como a maioria dos programadores do Delphi fazem que é incluir a diretiva {$R} próximo a instrução implementation. A documentação do Lazarus recomenda que deveria estar na seção initialization (provavelmente criaremos ela) na primeira unit a ser carregada dentro do projeto:

initialization
{$I arquivo.lrs}

E para usufruir do conteúdo há várias formas, para imagens é feita dessa forma:

procedure exampleproc;
var
  Image: TImage
begin
  Image := TImage.Create;
  // note que não precisamos da extensão
  Image.Picture.LoadFromLazarusResource('image1');
end;

A melhor forma de usá-lo é como mostra o exemplo acima, carregando o resource diretamente para o componente sem tê-lo de escrever no disco antes, parece que boa parte dos componentes que usam TGraphic do Lazarus foram adaptados para permitir o carregamento diretamente dos resources dessa forma. No Delphi faríamos isso usando um TStream.

Contudo, a vida não é só feita de figuras e às vezes usamos arquivos de outros formatos como .dll para garantir que o programa funcione corretamente. Então na carga inicial do programa precisaremos instruí-lo a extraí-los para a pasta adequada.

Iríamos na seção initialization (ou criamos ela) da primeira unit a ser carregada no projeto e faríamos a extração assim:

initialization
  {$I arquivo.lrs}
  RootDir:=ExtractFilepath(ParamStr(0));
  Extract_ResFileTo('image1',   RootDir+PathDelim+'img’+PathDelim+’image1.jpg');
  Extract_ResFileTo('image2',   RootDir+PathDelim+'img’+PathDelim+’image2.jpg');
  Extract_ResFileTo('minhaDLL', ExtractFilepath(ParamStr(0))+PathDelim+'minhaDLL.dll');

O exemplo acima usou a função Extract_ResFileTo que não existe no Lazarus, segue o código da mesma:

function Extract_ResFileTo(AResName: String; ASaveFileTo: String):String;
var
  res: TLResource;
  st: TLazarusResourceStream;
begin
  Result:=emptyStr;
  st := nil;
  if Result=emptyStr then
  begin
    if not DirectoryExists(ExtractFilePath(ASaveFileTo)) then
    begin
      // Cria o diretório de extração caso ele não exista
      if not ForceDirectories(ExtractFilePath(ASaveFileTo)) then
      begin
        Result:='Diretorio não existe: '+ExtractFilePath(ASaveFileTo);
      end;
    end;
  end;
  if Result=emptyStr then
  begin
    res:=LazarusResources.Find(AResName);
    if res=nil then
    begin
      Result:='RC com nome "'+AResName+'" não foi encontrada.';
    end
    else
    begin
      try
        st := TLazarusResourceStream.Create(AResName, nil);
        // salvando no disco
        st.SaveToFile(ASaveFileTo);
       finally
        st.Free;
      end;
    end;
  end;
end;

Você vai encontrar essa necessidade de extrair resources files para o disco ao usar componentes como o CEF4, MPlayer dentre outros que precisam que DLLs específicas estejam no diretório do executável do seu projeto. Claro que poderia copiá-las manualmente, mas isso é improdutivo e sujeito a erros então o melhor mesmo é pegar tais DLLs e embuti-las como arquivos de resources e quando o programa iniciar-se então verificar a existência das DLLs e caso não existam então extraí-las para a pasta adequada.

Créditos:
Adding resources to your program