fileinfo.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2013 by the Free Pascal development team
  4. File/Program version information routines.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit fileinfo;
  12. {$mode objfpc}
  13. {$h+}
  14. interface
  15. uses
  16. SysUtils, Classes, resource, versiontypes, versionresource;
  17. type
  18. // Low level interface
  19. { TVersionInfo }
  20. TVersionInfo = class
  21. private
  22. FResources : TResources;
  23. FVersionInfo : TVersionResource;
  24. procedure CheckLoaded;
  25. procedure FreeResources;
  26. function GetFixedInfo: TVersionFixedInfo;
  27. function GetStringFileInfo: TVersionStringFileInfo;
  28. function GetVarFileInfo: TVersionVarFileInfo;
  29. public
  30. constructor Create;
  31. destructor Destroy; override;
  32. procedure Load(Const Instance: THandle); overload;
  33. procedure Load(Const AFileName : string); overload;
  34. property FixedInfo: TVersionFixedInfo read GetFixedInfo;
  35. property StringFileInfo: TVersionStringFileInfo read GetStringFileInfo;
  36. property VarFileInfo: TVersionVarFileInfo read GetVarFileInfo;
  37. end;
  38. type
  39. // Higher level interface
  40. { TFileVersionInfo }
  41. TFileVersionInfo = class(TComponent)
  42. private
  43. FEnabled: Boolean;
  44. FFileName : String;
  45. FFilter : TStrings;
  46. FTranslation: String;
  47. FTranslationOnly: Boolean;
  48. FVersionStrings: TStrings;
  49. procedure CheckRead;
  50. Procedure FilterChange(Sender : TObject);
  51. procedure SetEnabled(AValue: Boolean);
  52. procedure SetFileName (Const AFileName : String);
  53. procedure SetFilter(AValue: TStrings);
  54. procedure SetTranslation(AValue: String);
  55. procedure SetTranslationOnly(AValue: Boolean);
  56. public
  57. constructor Create(AOwner: TComponent); override;
  58. destructor Destroy; override;
  59. Procedure Loaded; override;
  60. // Read info from file "FileName".
  61. procedure ReadFileInfo;
  62. published
  63. // If True, the info will be read as soon as a property changes.
  64. Property Enabled : Boolean Read FEnabled Write SetEnabled;
  65. // Filename to read version info from.
  66. property FileName : string read FFileName write SetFileName;
  67. // Extracted version information.
  68. property VersionStrings : TStrings read FVersionStrings;
  69. // Set of key namess to read. If Empty, all keys are read.
  70. property Filter : TStrings read FFilter Write SetFilter;
  71. // Translation to use. If none, take first language. After reading it will contain the used translation.
  72. property Translation : String read FTranslation Write SetTranslation;
  73. // If set to true, if the detected language is not found, an exception is raised.
  74. Property TranslationOnly : Boolean Read FTranslationOnly Write SetTranslationOnly;
  75. end;
  76. EVersionInfo = Class(Exception);
  77. { Convenience functions }
  78. TVersionQuad = Array[1..4] of Word; // Array version
  79. TProgramVersion = Record
  80. Major,Minor,Revision,Build : Word; // Record version
  81. end;
  82. // Compare result.
  83. TVersionCompare = (vcEqual, // Equal version
  84. vcBuildDiffers, // Build differs version
  85. vcRevisionDiffers, // At least revision differs
  86. vcMinorDiffers, // At least Minor version differs
  87. vcMajorDiffers // At least Major version differs
  88. );
  89. // Extract program version information in 1 call.
  90. Function GetProgramVersion (Var Version : TVersionQuad) : Boolean;
  91. Function GetProgramVersion (Var Version : TProgramVersion) : Boolean;
  92. // Compare 2 versions
  93. Function CompareVersionQuads(Quad1,Quad2 : TVersionQuad) : TVersionCompare;
  94. Function CompareProgramVersion(Version1,Version2 : TProgramVersion) : TVersionCompare;
  95. // Convert version quad to string
  96. Function VersionQuadToStr(Const Quad : TVersionQuad) : String;
  97. Function ProgramversionToStr(Const Version : TProgramVersion) : String;
  98. // Try to convert string to version quad.
  99. Function TryStrToVersionQuad(S : String; Var Quad : TVersionQuad) : Boolean;
  100. Function TryStrToProgramVersion(S : String; Var Version : TProgramVersion) : Boolean;
  101. // Convert string to version quad, raise exception if invalid string.
  102. Function StrToVersionQuad(Const S : String) : TVersionQuad;
  103. Function StrToProgramVersion(Const S : String ): TProgramVersion;
  104. // Check if a version is newer than another. Maybe convert to operators ?
  105. Function NewerVersion(Q1,Q2 : TVersionQuad) : Boolean;
  106. Function NewerVersion(V1,V2 : TProgramVersion) : Boolean;
  107. Function NewerVersion(V1,V2 : String) : Boolean;
  108. Operator := (q : TVersionQuad) : TProgramVersion;
  109. Operator := (V : TProgramVersion) : TVersionQuad;
  110. implementation
  111. Resourcestring
  112. SErrNoResourcesLoaded = 'No version info loaded';
  113. SErrNoTranslation = 'Translation "%s" not found in version strings.';
  114. SErrNotVersionQuad = 'Quadruple "%s" is not a valid version';
  115. { TVersionInfo }
  116. function TVersionInfo.GetFixedInfo: TVersionFixedInfo;
  117. begin
  118. CheckLoaded;
  119. Result:=FVersionInfo.FixedInfo;
  120. end;
  121. Procedure TVersionInfo.CheckLoaded;
  122. begin
  123. if (FVersionInfo=Nil) then
  124. Raise EVersionInfo.Create(SErrNoResourcesLoaded);
  125. end;
  126. function TVersionInfo.GetStringFileInfo: TVersionStringFileInfo;
  127. begin
  128. CheckLoaded;
  129. Result := FVersionInfo.StringFileInfo;
  130. end;
  131. function TVersionInfo.GetVarFileInfo: TVersionVarFileInfo;
  132. begin
  133. CheckLoaded;
  134. Result := FVersionInfo.VarFileInfo;
  135. end;
  136. constructor TVersionInfo.Create;
  137. begin
  138. inherited Create;
  139. end;
  140. destructor TVersionInfo.Destroy;
  141. begin
  142. FreeResources;
  143. inherited Destroy;
  144. end;
  145. procedure TVersionInfo.FreeResources;
  146. begin
  147. if Assigned(FResources) then
  148. FreeAndNil(FResources)
  149. else
  150. FreeAndNil(FVersionInfo);
  151. end;
  152. procedure TVersionInfo.Load(Const AFileName : String);
  153. Var
  154. I : Integer;
  155. begin
  156. FreeResources;
  157. FResources:=TResources.Create;
  158. FResources.LoadFromFile(AFileName);
  159. I:=0;
  160. While (FVersionInfo=Nil) and (I<FResources.Count) do
  161. begin
  162. if FResources.Items[i] is TVersionResource then
  163. FVersionInfo:=TVersionResource(FResources.Items[i]);
  164. Inc(I);
  165. end;
  166. // This will read the info.
  167. if assigned(FVersionInfo) then
  168. FVersionInfo.FixedInfo;
  169. end;
  170. procedure TVersionInfo.Load(Const Instance: THandle);
  171. var
  172. Stream: TResourceStream;
  173. begin
  174. FreeResources;
  175. Stream := TResourceStream.CreateFromID(Instance, 1, {$ifdef FPC_OS_UNICODE}PWideChar{$else}PChar{$endif}(RT_VERSION));
  176. try
  177. FVersionInfo:=TVersionResource.Create;
  178. FVersionInfo.SetCustomRawDataStream(Stream);
  179. // access some property to load from the stream
  180. FVersionInfo.FixedInfo;
  181. // clear the stream
  182. FVersionInfo.SetCustomRawDataStream(nil);
  183. finally
  184. Stream.Free;
  185. end;
  186. end;
  187. { initialize everything }
  188. constructor TFileVersionInfo.Create(AOwner: TComponent);
  189. begin
  190. inherited Create(AOwner);
  191. FVersionStrings := TStringList.Create;
  192. TStringList(FVersionStrings).Duplicates:=dupIgnore;
  193. FFilter:=TStringList.Create;
  194. TStringList(FFilter).Duplicates:= dupIgnore;
  195. TStringList(FFilter).OnChange:=@FilterChange;
  196. FFileName := '';
  197. end;
  198. destructor TFileVersionInfo.Destroy;
  199. begin
  200. FreeAndNil(FVersionStrings);
  201. FreeAndNil(FFilter);
  202. inherited;
  203. end;
  204. procedure TFileVersionInfo.Loaded;
  205. begin
  206. CheckRead;
  207. end;
  208. { Get filename, check if file exists and read info from file }
  209. procedure TFileVersionInfo.SetFileName (Const AFileName : string);
  210. begin
  211. FVersionStrings.clear;
  212. FFileName := AFileName;
  213. if FileExists(FFileName) or (FFileName='') then
  214. CheckRead;
  215. end;
  216. procedure TFileVersionInfo.SetEnabled(AValue: Boolean);
  217. begin
  218. if FEnabled=AValue then Exit;
  219. FEnabled:=AValue;
  220. CheckRead;
  221. end;
  222. { read info from file }
  223. procedure TFileVersionInfo.ReadFileInfo;
  224. Var
  225. VI : TVersionInfo;
  226. ST : TVersionStringTable;
  227. TI,I,J : Integer;
  228. S: String;
  229. begin
  230. FEnabled:=True;
  231. VI:=TVersionInfo.Create;
  232. try
  233. if (FileName<>'') and (FileName<>Paramstr(0)) then
  234. VI.Load(FileName)
  235. else
  236. VI.Load(HInstance);
  237. // If no Translation specified, then try to detect.
  238. If (FTranslation='') then
  239. begin
  240. if (VI.VarFileInfo.Count>0) then
  241. FTranslation:=Format('%.4x%.4x',[VI.VarFileInfo.Items[0].language,VI.VarFileInfo.Items[0].codepage]);
  242. end;
  243. if (FTranslation='') then
  244. begin
  245. // Take first language
  246. Ti:=0;
  247. if (VI.StringFileInfo.Count>0) then
  248. FTranslation:=VI.StringFileInfo.Items[0].Name
  249. end
  250. else
  251. begin
  252. // Look for index of language
  253. TI:=VI.StringFileInfo.Count-1;
  254. While (TI>=0) and (CompareText(VI.StringFileInfo.Items[Ti].Name,FTranslation)<>0) do
  255. Dec(Ti);
  256. If (TI<0) then
  257. begin
  258. if FTranslationOnly then
  259. Raise EVersionInfo.CreateFmt(SErrNoTranslation,[FTranslation]);
  260. TI:=0;
  261. FTranslation:=VI.StringFileInfo.Items[Ti].Name;
  262. end;
  263. end;
  264. ST:=VI.StringFileInfo.Items[Ti];
  265. for J:=0 to ST.Count-1 do
  266. if (FFilter.Count=0) or (FFilter.IndexOf(ST.Keys[j])<>-1) then
  267. FVersionStrings.Add(ST.Keys[j]+'='+ST.Values[j]);
  268. finally
  269. FreeAndNil(VI);
  270. end;
  271. end;
  272. procedure TFileVersionInfo.SetFilter(AValue: TStrings);
  273. begin
  274. if FFilter=AValue then Exit;
  275. FFilter.Assign(AValue);
  276. CheckRead;
  277. end;
  278. procedure TFileVersionInfo.SetTranslation(AValue: String);
  279. begin
  280. if FTranslation=AValue then Exit;
  281. FTranslation:=AValue;
  282. CheckRead;
  283. end;
  284. procedure TFileVersionInfo.SetTranslationOnly(AValue: Boolean);
  285. begin
  286. if FTranslationOnly=AValue then Exit;
  287. FTranslationOnly:=AValue;
  288. CheckRead;
  289. end;
  290. procedure TFileVersionInfo.CheckRead;
  291. begin
  292. if Enabled and not (csLoading in ComponentState) then
  293. ReadFileInfo;
  294. end;
  295. procedure TFileVersionInfo.FilterChange(Sender: TObject);
  296. begin
  297. CheckRead;
  298. end;
  299. { Convenience function }
  300. Function GetProgramVersion (Var Version : TVersionQuad) : Boolean;
  301. Var
  302. VI : TVersionInfo;
  303. I : Integer;
  304. begin
  305. Result:=False;
  306. VI:=TVersionInfo.Create;
  307. try
  308. try
  309. VI.Load(HInstance);
  310. For I:=1 to 4 do
  311. Version[i]:=VI.FixedInfo.FileVersion[I-1];
  312. Result:=True;
  313. except
  314. // Ignore
  315. end;
  316. finally
  317. VI.Free;
  318. end;
  319. end;
  320. Function GetProgramVersion (Var Version : TProgramVersion) : Boolean;
  321. Var
  322. VQ : TVersionQuad;
  323. begin
  324. Result:=GetProgramVersion(VQ);
  325. if Result then
  326. Version:=VQ;
  327. end;
  328. Function CompareVersionQuads(Quad1,Quad2 : TVersionQuad) : TVersionCompare;
  329. Const
  330. EqualResults : Array[1..4] of TVersionCompare =
  331. (vcMajorDiffers,vcMinorDiffers,vcRevisionDiffers,vcBuildDiffers);
  332. Var
  333. I : Integer;
  334. begin
  335. Result:=vcEqual;
  336. I:=1;
  337. While (Result=vcEqual) and (I<5) do
  338. If Quad1[i]<>Quad2[i] then
  339. Result:=EqualResults[i]
  340. else
  341. inc(I);
  342. end;
  343. Function CompareProgramVersion(Version1,Version2 : TProgramVersion) : TVersionCompare;
  344. Var
  345. Q1,Q2 : TVersionQuad;
  346. begin
  347. Q1:=Version1;
  348. Q2:=Version2;
  349. Result:=CompareVersionQuads(Q1,Q2);
  350. end;
  351. function PadVersion(const S: String): String;
  352. Var
  353. I,Dots : Integer;
  354. begin
  355. Dots:=0;
  356. For i:=1 to length(S) do
  357. if S[i]='.' then
  358. Inc(Dots);
  359. Result:=S;
  360. while (Dots<3) do
  361. begin
  362. Result:=result+'.0';
  363. Inc(Dots);
  364. end;
  365. end;
  366. function VersionQuadToStr(const Quad: TVersionQuad): String;
  367. begin
  368. Result:=Format('%d.%d.%d.%d',[Quad[1],Quad[2],Quad[3],Quad[4]]);
  369. end;
  370. Function ProgramversionToStr(Const Version : TProgramVersion) : String;
  371. begin
  372. Result:=Format('%d.%d.%d.%d',[Version.Major,Version.Minor,Version.Revision,Version.Build]);
  373. end;
  374. Function TryStrToProgramVersion(S : String; Var Version : TProgramVersion) : Boolean;
  375. Var
  376. Q : TVersionQuad;
  377. begin
  378. Result:=TryStrToVersionQuad(S,Q);
  379. if Result then
  380. Version:=Q;
  381. end;
  382. Function TryStrToVersionQuad(S : String; Var Quad : TVersionQuad) : Boolean;
  383. Var
  384. I,P,Dots,Q : Integer;
  385. begin
  386. Result:=True;
  387. FillChar(Quad,SizeOf(Quad),0);
  388. Dots:=0;
  389. I:=0;
  390. While Result and (S<>'') and (I<4) do
  391. begin
  392. inc(i);
  393. P:=Pos('.',S);
  394. If (P=0) then
  395. P:=Length(S)+1
  396. else
  397. inc(Dots);
  398. Q:=StrToIntDef(Copy(S,1,P-1),-1);
  399. Delete(S,1,P);
  400. Result:=Q<>-1;
  401. If Result then
  402. Quad[I]:=Q;
  403. end;
  404. Result:=(Dots=3);
  405. end;
  406. Function StrToVersionQuad(Const S : String) : TVersionQuad;
  407. begin
  408. if Not TryStrToVersionQuad(S,Result) then
  409. Raise EConvertError.CreateFmt(SErrNotVersionQuad,[S]);
  410. end;
  411. Function StrToProgramVersion(Const S : String ): TProgramVersion;
  412. begin
  413. Result:=StrToVersionQuad(S);
  414. end;
  415. Function NewerVersion(V1,V2 : TProgramVersion) : Boolean;
  416. Var
  417. Q1,Q2 : TversionQuad;
  418. begin
  419. Q1:=V1;
  420. Q2:=V2;
  421. Result:=Newerversion(Q1,Q2);
  422. end;
  423. Function NewerVersion(Q1,Q2 : TVersionQuad) : Boolean;
  424. begin
  425. Result:=False;
  426. Case CompareVersionQuads(Q1,Q2) of
  427. vcEqual : Result:=False;
  428. vcBuildDiffers : Result:=Q1[4]>Q2[4];
  429. vcRevisionDiffers : Result:=Q1[3]>Q2[3];
  430. vcMinorDiffers : Result:=Q1[2]>Q2[2];
  431. vcMajorDiffers : Result:=Q1[1]>Q2[1];
  432. end;
  433. end;
  434. function NewerVersion(V1, V2: String): Boolean;
  435. Var
  436. Q1,Q2 : TVersionQuad;
  437. begin
  438. if TryStrToVersionQuad(V1,Q1) and TryStrToVersionQuad(V2,Q2) then
  439. Result:=NewerVersion(Q1,Q2)
  440. else
  441. Result:=False;
  442. end;
  443. Operator := (q : TVersionQuad) : TProgramVersion;
  444. begin
  445. Result.Major:=Q[1];
  446. Result.Minor:=Q[2];
  447. Result.Revision:=Q[3];
  448. Result.Build:=Q[4];
  449. end;
  450. Operator := (V : TProgramVersion) : TVersionQuad;
  451. begin
  452. Result[1]:=V.Major;
  453. Result[2]:=V.Minor;
  454. Result[3]:=V.Revision;
  455. Result[4]:=V.Build;
  456. end;
  457. end.