суббота, 9 марта 2013 г.

Как получить версию приложения? (Delphi 7, Delphi XE)

Версия приложения. Классическая версия может содежать от двух до четырёх чисел:
A.B.C.D
 А — старшая (major) версия
 B — младшая (minor) версия
 C — релиз (который может отсутствовать - тогда на его место становится сборка)
 D — сборка.
Как можно получить номер версии в программе на Delphi?
Для того, чтобы контролировать версию приложения, она должна при сборке проекта сохраняться внутри *.exe файла. Для этого, во первых, в опциях проекта надо поставить галку напротив Include version information in project, во вторых, для того, чтобы номер сборки каждый раз увеличивался, надо поставить галку напротив Auto generate build number. Таким образом, каждый раз при сборке проекта, мы всегда будем получать новую версию.
В старых версиях Delphi работает вот такая функция (это проверялось на Delphi 7): 
 
function GetVersion(var Major, Minor, Release, Build: Word): Boolean;
var
  info: Pointer;
  infosize: DWORD;
  fileinfo: PVSFixedFileInfo;
  fileinfosize: DWORD;
  tmp: DWORD;
begin
  infosize := GetFileVersionInfoSize(PChar(ParamStr(0)), tmp);
  Result := infosize <> 0;
  if Result then
  begin
    GetMem(info, infosize);
    try
      GetFileVersionInfo(PChar(ParamStr(0)), 0, infosize, info);
      VerQueryValue(info, nil, Pointer(fileinfo), fileinfosize);
      Major := fileinfo.dwProductVersionMS shr 16;
      Minor := fileinfo.dwProductVersionMS and $FFFF;
      Release := fileinfo.dwProductVersionLS shr 16;
      Build := fileinfo.dwProductVersionLS and $FFFF;
    finally
      FreeMem(info, fileinfosize);
    end;
  end;
end;
Увы, в Delphi XE такой код приведет к исключению Access violation... Реализация данной функции для более новых версий Delphi может быть такой:
function MyVersion(Files: string): string;
var
  Buffer: string;
  fInfoSize: DWORD;
  function InitVersion: Boolean;
  var
    FilenamePointer: PChar;
  begin
    Result := true;
    FilenamePointer := PChar(Files);
    fInfoSize := GetFileVersionInfoSize(FilenamePointer, fInfoSize);
    if fInfoSize > 0 then
    begin
      SetLength(Buffer, fInfoSize);
      if not GetFileVersionInfo(FilenamePointer, 0, fInfoSize,
        PChar(Buffer)) then
      begin
        Result := False;
      end;
    end; // if
  end; // InitVersion
  function GetVersion(whatToGet: string): string;
  var
    tmpVersion: string;
    Len, Len2: DWORD;
    Value: PChar;
    temp: PLongInt;
    tempStr: string;
  begin
    Result := '';
    if fInfoSize > 0 then
    begin
      SetLength(tmpVersion, 200);
      Value := @tmpVersion;
      VerQueryValue(PChar(Buffer), '\VarFileInfo\Translation',
        Pointer(temp), Len2);
      tempStr := Format('%s%.4x%.4x\%s%s', ['\StringFileInfo\', LoWord(temp^),
        HiWord(temp^), whatToGet, #0]);
      if VerQueryValue(PChar(Buffer), PChar(tempStr), Pointer(Value), Len) then
        Result := Value;
    end; // if
  end; // getversion

begin
  Buffer := '';
  try
    InitVersion;
    Result := GetVersion('FileVersion');
  except
    Result := '';
  end;
end;
Приведенная функция работает нормально, версию возвращает сразу в строке. Аргумент функции - путь к *.exe файлу.
Функция, возможно, не оптимизирована, делалась на скорую руку.  Думаю, что в перспективе найду более компактное решение и опубликую. 
Удачи в программировании! А данный блог планируется, как своего рода записная книжка или, так сказать, книга рецептов на все случаи программисткой жизни. Таких рецептов много, но иногда бывает трудновато найти что-либо полезное...

1 комментарий:

  1. по теме есть информация на http://subversion.assembla.com/svn/wwf2/GoldApp/about.pas

    ОтветитьУдалить