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.