Qual é a versão?

Facilidade para obter a versão e outras propriedades de programas Delphi

Como você armazena a informação de versão do seu programa Delphi? Muitas vezes montamos controles internos nos sistemas para indicar a versão do sistema e informações da empresa. Você conhece a aba para configurar informações de versão, existente na tela de opções de projeto do Delphi? Neste artigo estarei disponibilizando uma classe que vai fazer todo este trabalho difícil para podermos recuperar informações em tempo de execução.

Configurando o ambiente para teste

Primeiro vamos criar um novo projeto no Delphi 7 (File -> New -> Application). No projeto criado acesse as opções de projeto, habilite o preenchimento de informações de versão, e coloque valores de teste. Se você quer usar o Delphi 2005, apenas crie uma aplicação VCL Win32, disponível para a personalidade Delphi.


Estas informações sobre versão podem ser adicionadas em qualquer tipo de projeto Delphi, seja um Executável (.exe), Biblioteca Dinâmica (.dll) ou Package (.bpl).

Salve este projeto na pasta de sua preferência. Já aproveite e faça download do código fonte do artigo, para poder utilizar a classe para obter informações de versão. Agora para completar o exemplo faça os seguintes passos:

1. Adicione ao seu projeto o arquivo “UInfoVersao.pas” (a classe criada para este artigo). Para isto, acesse o menu “Project -> Add to Project”.

2. Na tela principal da aplicação (formulário) vamos adicionar dois componentes, um chamado MemoInformacoes (TMemo) e outro chamado BtnMostraInformacoes (TButton).

3. Vamos acrescentar a unit UInfoVersao no nosso formulário principal, acessando o menu File> Use Unit, e selecionando o arquivo UInfoVersao.

4. Vamos criar o evento OnClick para o botão BtnMostraInformacoes e vamos colocar o código disponível na Listagem 1:

Listagem 1. Código do botão BtnMostraInformacões

procedure TFormPrincipal.BtnMostraInformacoesClick(Sender: TObject);

begin

  TInfoVersao.SetAppName(ExtractFilePath(Application.ExeName));

  MemoInformacoes.Lines.Text := TInfoVersao.getInstance().getProperties().Text;

end;

A classe TInfoVersao implementa o padrão de projeto Singleton, permitindo que apenas 1 cópia dela esteja disponível para toda aplicação. Através do método SetAppName, configuramos o nome do aplicativo que a classe deve obter informações. Com o código disponível na Listagem 1 estamos indicando para que a classe processe informações sobre o próprio programa de teste.

O método getInstance() da classe TInfoVersao serve para obter a cópia do objeto para que possamos perguntar coisas sobre a aplicação sendo analisada. O método getProperties() retorna uma TStringList, contendo todas informações que foram capturadas do executável. A partir do TStringList, poderíamos acessar a informação CompanyName usando por exemplo o comando ShowMessage(TInfoVersao.getInstance().getProperties().Values[‘CompanyName’]).

Veremos em seguida outras formas de obter estas informações. Rode o exemplo. Se tudo correr bem, você deve ver informações do executável disponibilizadas no Memo, como aparece na Listagem 2:

Listagem 2. Saída disponibilizada no componente MemoInformações

CompanyName=ClubeDelphi (Grupo DevMedia)

FileDescription=Informações sobre versão da Aplicação

FileVersion=1.1.3.2322

InternalName=InformacoesVersao

LegalCopyright=Copyright 2005 DevMedia

LegalTradeMarks=Delphi, ClubeDelphi

OriginalFilename=InformacoesVersao.exe

ProductName=TInfoVersao

ProductVersion=1.0.0.0

Comments=Demonstração da classe TInfoVersao, utilizada para obter informações da aplicação.

ReleaseDate=20 de Março de 2005

Verifique a última informação, ReleaseDate. Por padrão ela não está disponível no Delphi para preenchimento, mas nós podemos adicionar a informação que desejarmos. Para isto nas opções de projeto você pode clicar com o botão direito sobre as propriedades do aplicativo e criar novas chaves. Também pode remover chaves que não considera interessante. 

As informações de versão também podem ser acessadas via Windows Explorer, ao visualizar as propriedades do arquivo. 

Com isto conseguimos entender como funciona o teste da classe TInfoVersao e o processo de atribuir informações de versão para um projeto Delphi.

Entendo a classe TInfoVersão

Agora que a sua aplicação de teste funcionou, vamos entender o funcionamento da classe que obtém informações sobre o executável. A classe TInfoVersao possui uma série de propriedades definidas, uma para cada informação que desejamos ler da aplicação. Você pode e deve personalizar esta classe para as informações que são relevantes para você e o seu ambiente de desenvolvimento.

A forma de obter informações de versão se resume a chamar uma série de funções da API do Windows. No processo de desenvolvimento, padronizei o processo de busca de informações. Primeiro defini constantes, para facilitar o acesso as propriedades, que são identificadas por Strings. Utilizando uma constante para este fim facilita o processo de manutenções deste fonte. Veja na Listagem 3 a definição das constantes da versão inicial da TInfoVersao. Note os comentários padronizados para permitir a geração de documentação via PasDoc (veja mais informações na Edição 50 da ClubeDelphi).

Listagem 3. Constantes definidas pela aplicação

const

{: Nome da empresa}

ivCOMPANY_NAME = 'CompanyName';

{: Descrição do arquivo}

ivFILE_DESCRIPTION = 'FileDescription';

{: Versão do arquivo}

ivFILE_VERSION = 'FileVersion';

{: Nome interno}

ivINTERNAL_NAME = 'InternalName';

{: Direitos autorais}

ivLEGAL_COPYRIGHT = 'LegalCopyright';

{: Marcas registradas?}

ivLEGAL_TRADEMARKS = 'LegalTradeMarks';

{: Nome original}

ivORIGINAL_FILENAME = 'OriginalFilename';

{: Nome do Produto }

ivPRODUCT_NAME = 'ProductName';

{: Versão do produto}

ivPRODUCT_VERSION = 'ProductVersion';

{: Comentários}

ivCOMMENTS = 'Comments';

{: Data de release}

ivRELEASE_DATE = 'ReleaseDate';

As informações lidas do executável são armazenadas em um atributo privado chamado FInfoVersao, do tipo TStringList, usando a estrutura TStringList.Values desta classe. Existe um método que veremos mais a frente, chamado CarregaInfoVersao, que carrega informações para cada constante definida na aplicação.

Para cada propriedade definida na aplicação, definimos um método GetNomePropriedade e SetNomePropriedade, isto em virtude das propriedades não estarem ligadas a um campo específico da classe. Elas fazem acesso a estrutura mantida pelo campo FInfoVersao. Veja na Listagem 4 as estruturas necessárias para definir a propriedade CompanyName.

Listagem 4. Definição da propriedade CompanyName

interface

const

    {: Nome da empresa}

    ivCOMPANY_NAME = 'CompanyName';

type

    TInfoVersao = class(TObject)

    private

           {: Armazena as informações lidas de uma determinada aplicação}

           FInfoVersao: TStringList;

    public

           {: Nome da empresa. }

           property CompanyName: string read GetCompanyName write SetCompanyName;

    end;

implementation

function TInfoVersao.GetCompanyName: string;

begin

 result := FInfoVersao.Values[ivCOMPANY_NAME];

end;

procedure TInfoVersao.SetCompanyName(const Value: string);

begin

 FInfoVersao.Values[ivCOMPANY_NAME] := Value;

end;

Para poder atribuir um valor para a propriedade CompanyName a partir da leitura de informações de versão do aplicativo, temos que realizar os seguintes passos:

1. Obter o tamanho da estrutura que possui informações sobre o executável. Se ela for maior que zero significa que o aplicativo possui algum tipo de informação definida. Para isto usamos a função GetFileVersionInfoSize da API do Windows.

2. A partir da função que retornou o tamanho, precisamos alocar memória para um Buffer. Usaremos uma variável do tipo PChar e a função AllocMem para realizar a alocação de memória.

3. O próximo passo é obter as informações de versão, e armazenar estas no Buffer recém alocado. Para isto chamamos a função GetFileVersionInfo também da API do Windows.

4. Depois finalmente, vamos acessar o buffer para buscar uma informação específica. Usaremos a função VerQueryValue para buscar este valor. Se o conteúdo lido tem o tamanho maior que zero, quer dizer que a informação requisitada está disponível.

Veja o fonte do método TInfoVersao.getPropertyValue, que retorna o valor de uma determinada propriedade, na Listagem 5.

Listagem 5. Fonte do método TInfoVersão.getPropertyValue

function TInfoVersao.getPropertyValue(propName: string): string;

var

 {: Tamanho total da estrutura de informações de versão. }

 infoSize: Cardinal;

 {: Estrutura de informações de versão. }

 buffer: PChar;

 {: Para cada informação requisitada no estilo variavel=valor, esta variável

 armazena o valor. }

 valorLido: PChar;

 {: Para cada informação lida da estrutura de versões, que é armazenada em

 valorLido, esta variável possui o tamanho desta estrutura que foi lida.}

 tamanhoValorLido: Cardinal;

begin

 result := '';

 // obtem o tamanho das informações disponíveis no executável.

 infoSize := GetFileVersionInfoSize(PAnsiChar(appName), infoSize);

 // sim, existe informação de versão neste arquivo.

 if infoSize > 0 then

 begin

   // aloca a memória necessária para ler informações de versão

   buffer := AllocMem(infoSize);

   try

     // obtem as informações de versão

     GetFileVersionInfo(PChar(appName), 0, infoSize, buffer);

     if VerQueryValue(buffer, PChar('StringFileInfo\041604E4\' +

     propName), Pointer(valorLido), tamanhoValorLido) then

     begin

       valorLido := PChar(Trim(valorLido));

       // se existe algum valor lido, atualiza StringList com informações.

       if Length(valorLido) > 0 then

          result:= valorLido;

     end;

   finally

      // libera memória alocada no início do processo.

      FreeMem(buffer, infoSize);

   end;

 end;

end;

A diferença desta função para a função que obtém informações de todas as propriedades definidas, é a definição de um vetor e um laço for para consultar o valor de todos os atributos definidos nas constantes (várias chamadas para a função VerQueryValue). Veja a Listagem 6 com a definição deste vetor, dentro do método CarregaInfoVersao.

Listagem 6. Cabeçalho da função CarregaInfoVersão

procedure TInfoVersao.CarregaInfoVersao;

const

  {: Informação que serão analisadas por padrão. Antes de colocar uma nova

  informação aqui, cadastrar nova constante.}

  Informacoes: array[1..11] of string =

  (ivCOMPANY_NAME, ivFILE_DESCRIPTION, ivFILE_VERSION,

  ivINTERNAL_NAME, ivLEGAL_COPYRIGHT, ivLEGAL_TRADEMARKS,

  ivORIGINAL_FILENAME, ivPRODUCT_NAME, ivPRODUCT_VERSION,

  ivCOMMENTS, ivRELEASE_DATE);

begin

//...

end;


Conclusões

Verificamos mais uma classe para ajudar você no desenvolvimento de sistemas e sempre incrementar as formas de suporte ao seu cliente. Dica de uso desta classe: Você pode criar um outro sistema, utilitário, que consulte as informações de versão dos seus sistemas e componentes destes (seus executáveis, DLLs e BPLs), e consulte algum site para saber se existem atualizações disponíveis para o usuário.

Links

pasdoc.sourceforge.net/

Site do PasDoc, ferramenta para formatação de código. Mais informações podem ser consultadas na edição 50 da ClubeDelphi.