home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue38 / System / VersionInfo.pas
Encoding:
Pascal/Delphi Source File  |  1998-08-12  |  8.5 KB  |  253 lines

  1. unit VersionInfo;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  7.   Dialogs, ExtCtrls, ExptIntf, ToolIntf;
  8.  
  9. type
  10.     TVersionInfo = class(TComponent)
  11.     private
  12.         { Private declarations }
  13.         fTimer: TTimer;
  14.         fFileAge: Integer;
  15.         fVersionData: PChar;
  16.         fVerStrings: TStringList;
  17.         fExecutableFileName: String;
  18.         procedure ReadOnlyStringProperty (Index: Integer; const Value: String);
  19.         procedure ReadOnlyIntegerProperty (Index, Value: Integer);
  20.         procedure SetVersionStrings (Value: TStringList);
  21.         function GetIndexStringProperty (Index: Integer): String;
  22.         function GetIndexIntegerProperty (Index: Integer): Integer;
  23.         procedure TimerRefresh (Sender: TObject);
  24.         procedure Refresh;
  25.         procedure ParseVersionData;
  26.         function GetKey (const KeyName: String): String;
  27.     protected
  28.         { Protected declarations }
  29.     public
  30.         { Public declarations }
  31.         constructor Create (AOwner: TComponent); override;
  32.         destructor Destroy; override;
  33.         property Key [const KeyName: String]: String read GetKey;
  34.     published
  35.         { Published declarations }
  36.         property ExecutableFileName: String index 0 read GetIndexStringProperty write ReadOnlyStringProperty;
  37.         property CompanyName: String index 1 read GetIndexStringProperty write ReadOnlyStringProperty;
  38.         property FileDescription: String index 2 read GetIndexStringProperty write ReadOnlyStringProperty;
  39.         property FileVersion: String index 3 read GetIndexStringProperty write ReadOnlyStringProperty;
  40.         property InternalName: String index 4 read GetIndexStringProperty write ReadOnlyStringProperty;
  41.         property LegalCopyright: String index 5 read GetIndexStringProperty write ReadOnlyStringProperty;
  42.         property LegalTrademarks: String index 6 read GetIndexStringProperty write ReadOnlyStringProperty;
  43.         property OriginalFilename: String index 7 read GetIndexStringProperty write ReadOnlyStringProperty;
  44.         property ProductName: String index 8 read GetIndexStringProperty write ReadOnlyStringProperty;
  45.         property ProductVersion: String index 9 read GetIndexStringProperty write ReadOnlyStringProperty;
  46.         property Comments: String index 10 read GetIndexStringProperty write ReadOnlyStringProperty;
  47.         property Keys: TStringList read fVerStrings write SetVersionStrings;
  48.         property FileVersionHigh: Integer index $30 read GetIndexIntegerProperty write ReadOnlyIntegerProperty;
  49.         property FileVersionLow: Integer index $34 read GetIndexIntegerProperty write ReadOnlyIntegerProperty;
  50.         property ProductVersionHigh: Integer index $38 read GetIndexIntegerProperty write ReadOnlyIntegerProperty;
  51.         property ProductVersionLow: Integer index $3C read GetIndexIntegerProperty write ReadOnlyIntegerProperty;
  52.     end;
  53.  
  54. procedure Register;
  55.  
  56. implementation
  57.  
  58. { TVersionInfo }
  59.  
  60. constructor TVersionInfo.Create (AOwner: TComponent);
  61. begin
  62.     Inherited Create (AOwner);
  63.     fFileAge := -1;
  64.     fVersionData := Nil;
  65.     fVerStrings := TStringList.Create;
  66.     if ToolServices = Nil then begin
  67.         // Run-time case
  68.         fExecutableFileName := Application.ExeName;
  69.         Refresh;
  70.     end else begin
  71.         // Design-time case
  72.         fTimer := TTimer.Create (Self);
  73.         fTimer.Enabled := True;
  74.         fTimer.Interval := 500;
  75.         fTimer.OnTimer := Self.TimerRefresh;
  76.         fExecutableFileName := ToolServices.GetProjectName;
  77.         if fExecutableFileName <> '' then fExecutableFileName := ChangeFileExt (fExecutableFileName, '.exe');
  78.     end;
  79. end;
  80.  
  81. destructor TVersionInfo.Destroy;
  82. begin
  83.     if ToolServices <> Nil then fTimer.Free;
  84.     if fVersionData <> Nil then FreeMem (fVersionData);
  85.     fVerStrings.Free;
  86.     Inherited Destroy;
  87. end;
  88.  
  89. procedure TVersionInfo.Refresh;
  90. var
  91.     pSrc: PChar;
  92.     hMod: hModule;
  93.     Res: hRsrc;
  94.     lRes: hGlobal;
  95. begin
  96.     // Trash the existing version data buffer
  97.     if fVersionData <> Nil then FreeMem (fVersionData);
  98.     fVersionData := Nil;
  99.  
  100.     // Now get the updated stuff....
  101.     if fExecutableFileName <> '' then begin
  102.         hMod := LoadLibraryEx (PChar (fExecutableFileName), 0, Load_Library_As_DataFile);
  103.         if hMod <> 0 then try
  104.             Res := FindResource (hMod, PChar (1), rt_Version);
  105.             if Res <> 0 then begin
  106.                 lRes := LoadResource (hMod, Res);
  107.                 if lRes <> 0 then begin
  108.                     pSrc := LockResource (lRes);
  109.                     if pSrc <> Nil then begin
  110.                         // Sanity check time!
  111.                         if PWideChar (pSrc + 6) = 'VS_VERSION_INFO' then begin
  112.                             GetMem (fVersionData, SizeofResource (hmod, Res));
  113.                             Move (pSrc^, fVersionData^, SizeofResource (hmod, Res));
  114.                             ParseVersionData;
  115.                         end;
  116.                     end;
  117.                 end;
  118.             end;
  119.         finally
  120.             FreeLibrary (hMod);
  121.         end;
  122.     end;
  123. end;
  124.  
  125. procedure TVersionInfo.ReadOnlyStringProperty (Index: Integer; const Value: String);
  126. begin
  127.     // Read-only property
  128. end;
  129.  
  130. procedure TVersionInfo.ReadOnlyIntegerProperty (Index, Value: Integer);
  131. begin
  132.     // Read-only property
  133. end;
  134.  
  135. procedure TVersionInfo.SetVersionStrings (Value: TStringList);
  136. begin
  137.     // Read-only property
  138. end;
  139.  
  140. procedure TVersionInfo.TimerRefresh (Sender: TObject);
  141. var
  142.     AgeNow: Integer;
  143. begin
  144.     AgeNow := FileAge (fExecutableFileName);
  145.     if AgeNow <> fFileAge then begin
  146.         // Executable has been freshened, newly created or deleted
  147.         fFileAge := AgeNow;
  148.         Refresh;
  149.     end;
  150. end;
  151.  
  152. procedure TVersionInfo.ParseVersionData;
  153. const
  154.     //---------------------------------------------------
  155.     // ACHTUNG!  Don't change these constants unless the
  156.     // format of the VERSION resource is altered.
  157.     //---------------------------------------------------
  158.     vSFIStart = $5C;              // Start of StringFileInfo block
  159.     vSTStart  = vSFIStart + $24;  // Start of String table block
  160.     vSStart   = vSTStart + $18;   // Start of String table proper
  161. var
  162.     p: PChar;
  163.     pw: PWord absolute p;
  164.     StringFileInfoLen, ThisEntryLen: Word;
  165.     Key, Val: String;
  166.  
  167.     function Align32 (p: PChar): PChar;
  168.     var
  169.         pp: LongInt absolute p;
  170.     begin
  171.         pp := (pp + 3) and $fffffffc;
  172.         Result := p;
  173.     end;
  174.  
  175. begin
  176.     // You can never have too many sanity checks...
  177.     if PWideChar (fVersionData + vSFIStart + 6) <> 'StringFileInfo' then
  178.         raise Exception.Create ('Unrecognised version block');
  179.     // Looks good - parse the version strings
  180.     fVerStrings.Clear;
  181.     p := fVersionData + vSTStart;
  182.     StringFileInfoLen := pw^;
  183.     // Point at first entry
  184.     p := fVersionData + vSStart;
  185.     while p < (fVersionData + vSTStart + StringFileInfoLen) do begin
  186.         ThisEntryLen := pw^;
  187.         Key := PWideChar (p + 6);
  188.         Val := PWideChar ((Align32 (p + 6 + ((Length (Key) + 1) * 2))));
  189.         fVerStrings.Add (Key + '=' + Val);
  190.         p := Align32 (p + ThisEntryLen);
  191.     end;
  192. end;
  193.  
  194. function TVersionInfo.GetKey (const KeyName: String): String;
  195. var
  196.     S: String;
  197.     Index, nPos: Integer;
  198. begin
  199.     if fVersionData = Nil then Result := '--not available--' else begin
  200.        for Index := 0 to fVerStrings.Count - 1 do begin
  201.            S := fVerStrings [Index];
  202.            nPos := Pos ('=', S);
  203.            if Copy (S, 1, nPos - 1) = KeyName then begin
  204.                Result := Copy (S, nPos + 1, MaxInt);
  205.                Exit;
  206.            end;
  207.        end;
  208.  
  209.        Result := '';
  210.     end;
  211. end;
  212.  
  213. function TVersionInfo.GetIndexStringProperty (Index: Integer): String;
  214. const
  215.     PropName: array [1..10] of String = (
  216.  
  217.               'CompanyName',
  218.               'FileDescription',
  219.               'FileVersion',
  220.               'InternalName',
  221.               'LegalCopyright',
  222.               'LegalTrademarks',
  223.               'OriginalFilename',
  224.               'ProductName',
  225.               'ProductVersion',
  226.               'Comments'                );
  227. begin
  228.     case Index of
  229.         0:     Result := fExecutableFileName;
  230.         1..10: Result := GetKey (PropName [Index]);
  231.     end;
  232. end;
  233.  
  234. function TVersionInfo.GetIndexIntegerProperty (Index: Integer): Integer;
  235. begin
  236.     if fVersionData = Nil then Result := -1 else
  237.        Result := PInteger (fVersionData + Index)^;
  238. end;
  239.  
  240. procedure Register;
  241. begin
  242.     RegisterComponents ('The X Factor', [TVersionInfo]);
  243. end;
  244.  
  245. end.
  246.  
  247.  
  248.  
  249.  
  250.  
  251.  
  252.  
  253.