Subversion Repositories spacemission

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 daniel-mar 1
{ ##
2
  @FILE                     PJVersionInfo.pas
3
  @COMMENTS                 Version Information Component (32 bit) source code
4
                            (development split from 16 bit version after v1.0).
5
  @PROJECT_NAME             Version Information Component
6
  @PROJECT_DESC             Component that reads version information from files.
7
  @OTHER_NAMES              + Original unit name was VerInfo.pas
8
                            + Changed to VInfo.pas at v2.1
9
                            + Changed to PJVersionInfo.pas at v3.0
10
  @AUTHOR                   Peter Johnson, LLANARTH, Ceredigion, Wales, UK
11
  @EMAIL                    peter.johnson@openlink.org
12
  @WEBSITE                  http://www.pjsoft.contactbox.co.uk/
13
  @COPYRIGHT                © 1998-2002, Peter D Johnson.
14
  @LEGAL_NOTICE             This component is placed in the public domain. It
15
                            may be freely copied and circulated on a not for
16
                            profit basis providing that the code is unmodified
17
                            and this notice and information about the author and
18
                            his copyright remains attached to the source code.
19
  @CREDITS                  In producing this component some techniques were
20
                            used which were learned from FVersion by PJ Veger,
21
                            Best, The Netherlands (Feb/96). In particular the
22
                            method of accessing language and char-set tables was
23
                            taken from PJ Veger's code.
24
  @HISTORY(
25
    @REVISION(
26
      @VERSION              1.0
27
      @DATE                 25/04/1998
28
      @COMMENTS             Original version - 16 bit only.
29
    )
30
    @REVISION(
31
      @VERSION              2.0
32
      @DATE                 06/12/1998
33
      @COMMENTS             Revised for use with 32 bit Windows. Not compatible
34
                            with v1.
35
    )
36
    @REVISION(
37
      @VERSION              2.0.1
38
      @DATE                 09/04/1999
39
      @COMMENTS             Changed installation palette from "Own" to "PJ
40
                            Stuff".
41
    )
42
    @REVISION(
43
      @VERSION              2.1
44
      @DATE                 28/11/1999
45
      @COMMENTS             + Changed unit name to VInfo from VerInfo to allow
46
                              component to install under Delphi 3 & 4 (VerInfo
47
                              clashes with an existing unit in these versions).
48
                            + Removed superfluous conditional compilation
49
                              directives.
50
    )
51
    @REVISION(
52
      @VERSION              3.0
53
      @DATE                 17/02/2002
54
      @COMMENTS             Major update:
55
                            + Added ability to access all "translations" stored
56
                              in a file's version information, rather than just
57
                              first one. This has been done so that code using
58
                              earlier versions of this component should continue
59
                              to work unchanged.
60
                            + Added new property to expose fixed file
61
                              information record.
62
                            + Added new "string array" property to give access
63
                              to string information by name: this property can
64
                              access any custom string information if the name
65
                              is known.
66
                            + Added properties to return number of
67
                              "translations" and to select index of
68
                              "translation" to be used.
69
                            + Added properites to return language and character
70
                              set codes in addition to descriptive strings.
71
                            + All string info, language and character set
72
                              properties now return values from the currently
73
                              selected translation (which defaults to the first
74
                              translation maintaining backward compatibilty).
75
                            + Empty file name string now accesses name of host
76
                              application per command line rather than using
77
                              Application.ExeName. This enables this code to
78
                              work correctly even if user changes name of
79
                              executable file.
80
                            + CharSet property now returns '' for unknown value
81
                              rather than 'Unknown'
82
                            + Renamed unit to PJVersionInfo from VInfo.
83
                            + Renamed TVersionNumber record to TPJVersionNumber
84
                            + Replaced Tvs_FixedFileInfo record with use of
85
                              Windows unit defined type TVSFixedFileInfo.
86
                            + Changed component palette from PJ Stuff to PJSoft
87
    )
88
  )
89
}
90
 
91
 
92
unit PJVersionInfo;
93
 
94
interface
95
 
96
uses
97
  // Delphi
98
  Windows, Classes;
99
 
100
type
101
 
102
  {
103
  TPJVersionNumber:
104
    Record holding version numbers.
105
  }
106
  TPJVersionNumber = record
107
    V1, V2, V3, V4: WORD;
108
  end;
109
 
110
  {
111
  TPJVersionInfo:
112
    Component that exposes the version information embedded in an executable
113
    file and exposed the detail as properties.
114
  }
115
  TPJVersionInfo = class(TComponent)
116
  private // properties
117
    fFileName: string;
118
    fHaveInfo: Boolean;
119
    fNumTranslations: Integer;
120
    fCurrentTranslation: Integer;
121
    fFixedFileInfo: TVSFixedFileInfo;
122
    procedure SetFileName(AName: string);
123
    function GetProductVersionNumber: TPJVersionNumber;
124
    function GetFileVersionNumber: TPJVersionNumber;
125
    function GetLanguage: string;
126
    function GetCharSet: string;
127
    function GetCharSetCode: WORD;
128
    function GetLanguageCode: WORD;
129
    function GetCurrentTranslation: Integer;
130
    procedure SetCurrentTranslation(const Value: Integer);
131
    function GetStringFileInfo(const Name: string): string;
132
    function GetStringFileInfoByIdx(Index: Integer): string;
133
  private
134
    fPInfoBuffer: PChar;
135
      {Pointer to info buffer}
136
    fPTransBuffer: Pointer;
137
      {Pointer to translation buffer}
138
    procedure GetInfoBuffer(Len: DWORD);
139
      {Creates an info buffer of required size}
140
    procedure GetTransBuffer(Len: UINT);
141
      {Creates a translation table buffer of required size}
142
    function GetTransStr: string;
143
      {Translation info encoded in string: takes into account current
144
      translation}
145
  protected
146
    procedure ClearProperties; virtual;
147
      {Forces properties to return cleared values}
148
    procedure ReadVersionInfo; virtual;
149
      {Reads version info from file}
150
  public
151
    constructor Create(AOwner: TComponent); override;
152
      {Class constructor: sets default values}
153
    destructor Destroy; override;
154
      {Class destructor: frees allocated memory}
155
    {Properties}
156
    property HaveInfo: Boolean
157
      read fHaveInfo;
158
      {Property true if file version info for the file per FileName property has
159
      been successfully read}
160
    property FixedFileInfo: TVSFixedFileInfo
161
      read fFixedFileInfo;
162
      {Exposes the whole fixed file info record: following properties expose
163
      the various fields of it}
164
    property FileVersionNumber: TPJVersionNumber read
165
      GetFileVersionNumber;
166
      {Version number of file, in numeric format, from fixed file info}
167
    property ProductVersionNumber: TPJVersionNumber
168
      read GetProductVersionNumber;
169
      {Version number of product, in numeric format, from fixed file info}
170
    property FileOS: DWORD
171
      read fFixedFileInfo.dwFileOS;
172
      {Code describing operating system to be used by file}
173
    property FileType: DWORD
174
      read fFixedFileInfo.dwFileType;
175
      {Code descibing type of file}
176
    property FileSubType: DWORD
177
      read fFixedFileInfo.dwFileSubType;
178
      {Code describing sub-type of file - only used for certain values of
179
      FileType property}
180
    property FileFlagsMask: DWORD
181
      read fFixedFileInfo.dwFileFlagsMask;
182
      {Code describing which FileFlags are valid}
183
    property FileFlags: DWORD
184
      read fFixedFileInfo.dwFileFlags;
185
      {Flags describing file state}
186
    property Comments: string index 0
187
      read GetStringFileInfoByIdx;
188
      {String file info property giving user defined comments for current
189
      translation}
190
    property CompanyName: string index 1
191
      read GetStringFileInfoByIdx;
192
      {String file info property giving name of company for current translation}
193
    property FileDescription: string index 2
194
      read GetStringFileInfoByIdx;
195
      {String file info property giving description of file for current
196
      translation}
197
    property FileVersion: string index 3
198
      read GetStringFileInfoByIdx;
199
      {String file info property giving version number of file in string format
200
      for current translation}
201
    property InternalName: string index 4
202
      read GetStringFileInfoByIdx;
203
      {String file info property giving internal name of file for current
204
      translation}
205
    property LegalCopyright: string index 5
206
      read GetStringFileInfoByIdx;
207
      {String file info property giving copyright message for current
208
      translation}
209
    property LegalTrademarks: string index 6
210
      read GetStringFileInfoByIdx;
211
      {String file info property giving trademark info for current translation}
212
    property OriginalFileName: string index 7
213
      read GetStringFileInfoByIdx;
214
      {String file info property giving original name of file for current
215
      translation}
216
    property PrivateBuild: string index 8
217
      read GetStringFileInfoByIdx;
218
      {String file info property giving information about a private build of
219
      file for current translation}
220
    property ProductName: string index 9
221
      read GetStringFileInfoByIdx;
222
      {String file info property giving name of product for current translation}
223
    property ProductVersion: string index 10
224
      read GetStringFileInfoByIdx;
225
      {String file info property giving version number of product in string
226
      format for current translation}
227
    property SpecialBuild: string index 11
228
      read GetStringFileInfoByIdx;
229
      {String file info property giving information about a special build of
230
      file for current translation}
231
    property StringFileInfo[const Name: string]: string
232
      read GetStringFileInfo;
233
      {Returns the value for string file info with given name for current
234
      translation. This property can return both standard and custom string
235
      info}
236
    property Language: string
237
      read GetLanguage;
238
      {Name of language in use in current translation}
239
    property CharSet: string
240
      read GetCharSet;
241
      {Name of character set in use in current translation}
242
    property LanguageCode: WORD
243
      read GetLanguageCode;
244
      {Code of laguage in use in current translation}
245
    property CharSetCode: WORD
246
      read GetCharSetCode;
247
      {Character set code in use in current translation}
248
    property NumTranslations: Integer
249
      read fNumTranslations;
250
      {The number of difference translations (ie languages and char sets) in
251
      the version information}
252
    property CurrentTranslation: Integer
253
      read GetCurrentTranslation write SetCurrentTranslation;
254
      {Zero-based index of the current translation: this is 0 when a file is
255
      first accessed. Set to a value in range 0..NumTranslations-1 to access
256
      other translations. All string info, language and char set properties
257
      return information for the current translation}
258
  published
259
    property FileName: string read fFileName write SetFileName;
260
      {Name of file to which version information relates}
261
  end;
262
 
263
procedure Register;
264
  {Register this component}
265
 
266
implementation
267
 
268
uses
269
  // Delphi
270
  SysUtils;
271
 
272
{ Component registration routine }
273
 
274
procedure Register;
275
  {Register this component}
276
begin
277
  RegisterComponents('PJSoft', [TPJVersionInfo]);
278
end;
279
 
280
{ TPJVersionInfo }
281
 
282
type
283
  {
284
  TTransRec:
285
    Record of language code and char set codes that are returned from version
286
    information.
287
  }
288
  TTransRec = packed record      // record to hold translation information
289
    Lang, CharSet: Word;
290
  end;
291
  {
292
  TTransRecs:
293
    Type used to type cast translation data into an array of translation
294
    records.
295
  }
296
  TTransRecs = array[0..1000] of TTransRec;
297
  {
298
  PTransRecs:
299
    Pointer to an array of translation records.
300
  }
301
  PTransRecs = ^TTransRecs;
302
 
303
procedure TPJVersionInfo.ClearProperties;
304
  {Forces properties to return cleared values}
305
begin
306
  // Record that we haven't read ver info: this effectively clears properties
307
  // since each property read access method checks this flag before returning
308
  // result
309
  fHaveInfo := False;
310
end;
311
 
312
constructor TPJVersionInfo.Create(AOwner: TComponent);
313
  {Class constructor: sets default values}
314
begin
315
  inherited Create(AOwner);
316
  // Default is no file name - refers to executable file for application
317
  FileName := '';
318
end;
319
 
320
destructor TPJVersionInfo.Destroy;
321
  {Class destructor: frees allocated memory}
322
begin
323
  // Ensure that info buffer is freed if allocated
324
  if fPInfoBuffer <> nil then
325
    StrDispose(fPInfoBuffer);
326
  // Ensure that translation buffer is free if allocated
327
  if fPTransBuffer <> nil then
328
    FreeMem(fPTransBuffer);
329
  inherited Destroy;
330
end;
331
 
332
function TPJVersionInfo.GetCharSet: string;
333
  {Read access method for CharSet property: return string describing character
334
  set if we have some version info and empty string if we haven't}
335
const
336
  // Map code numbers to char-set names
337
  cCharSets: array[0..11] of record
338
    Code: Word;   // char set code
339
    Str: string;  // associated name of char set
340
  end = (
341
    ( Code:    0; Str: '7-bit ASCII'                        ),
342
    ( Code:  932; Str: 'Windows, Japan (Shift - JIS X-0208)'),
343
    ( Code:  949; Str: 'Windows, Korea (Shift - KSC 5601)'  ),
344
    ( Code:  950;       Str: 'Windows, Taiwan (GB5)'              ),
345
    ( Code: 1200;       Str: 'Unicode'                            ),
346
    ( Code: 1250;       Str: 'Windows, Latin-2 (Eastern European)'),
347
    ( Code: 1251;       Str: 'Windows, Cyrillic'                  ),
348
    ( Code: 1252;       Str: 'Windows, Multilingual'              ),
349
    ( Code: 1253;       Str: 'Windows, Greek'                     ),
350
    ( Code: 1254;       Str: 'Windows, Turkish'                   ),
351
    ( Code: 1255;       Str: 'Windows, Hebrew'                    ),
352
    ( Code: 1256;       Str: 'Windows, Arabic'                    )
353
  );
354
var
355
  I: Integer; // loop control
356
begin
357
  Result := '';
358
  if fHaveInfo then
359
  begin
360
    // We've have ver info: scan table of codes looking for required entry
361
    for I := Low(cCharSets) to High(cCharSets) do
362
      if GetCharSetCode = cCharSets[I].Code then
363
      begin
364
        // found one - record its name
365
        Result := cCharSets[I].Str;
366
        Exit;
367
      end;
368
  end;
369
end;
370
 
371
function TPJVersionInfo.GetCharSetCode: WORD;
372
  {Read access for CharSetCode property: returns char set code for current
373
  translation or 0 if there is no translation or we have no version info}
374
begin
375
  if fHaveInfo and (GetCurrentTranslation >= 0) then
376
    Result := PTransRecs(fPTransBuffer)^[GetCurrentTranslation].CharSet
377
  else
378
    Result := 0;
379
end;
380
 
381
function TPJVersionInfo.GetCurrentTranslation: Integer;
382
  {Read access method for CurrentTranslation property: returns index to current
383
  translation if we have version info or -1 if no version info}
384
begin
385
  if fHaveInfo then
386
    Result := fCurrentTranslation
387
  else
388
    Result := -1;
389
end;
390
 
391
function TPJVersionInfo.GetFileVersionNumber: TPJVersionNumber;
392
  {Read access method for FileVersionNumber property: fill version info
393
  structure and return it - if there's no version info all values will be zero}
394
begin
395
  Result.V1 := HiWord(fFixedFileInfo.dwFileVersionMS);
396
  Result.V2 := LoWord(fFixedFileInfo.dwFileVersionMS);
397
  Result.V3 := HiWord(fFixedFileInfo.dwFileVersionLS);
398
  Result.V4 := LoWord(fFixedFileInfo.dwFileVersionLS);
399
end;
400
 
401
procedure TPJVersionInfo.GetInfoBuffer(Len: DWORD);
402
  {Creates an info buffer of required size}
403
begin
404
  // Clear any existing buffer
405
  if fPInfoBuffer <> nil then
406
    StrDispose(fPInfoBuffer);
407
  // Create the new one
408
  fPInfoBuffer := StrAlloc(Len);
409
end;
410
 
411
function TPJVersionInfo.GetLanguage: string;
412
  {Read access method for Language property: return string describing language
413
  if we have some version info and empty string if we haven't}
414
var
415
  Buf: array[0..255] of char;   // stores langauge string from API call
416
begin
417
  // Assume failure
418
  Result := '';
419
  // Try to get language name from Win API if we have ver info
420
  if fHaveInfo and (VerLanguageName(GetLanguageCode, Buf, 255) > 0) then
421
    Result := Buf;
422
end;
423
 
424
function TPJVersionInfo.GetLanguageCode: WORD;
425
  {Read access for LanguageCode property: returns language code for current
426
  translation or 0 if there is no translation or we have no version info}
427
begin
428
  if fHaveInfo and (GetCurrentTranslation >= 0) then
429
    Result := PTransRecs(fPTransBuffer)^[GetCurrentTranslation].Lang
430
  else
431
    Result := 0;
432
end;
433
 
434
function TPJVersionInfo.GetProductVersionNumber: TPJVersionNumber;
435
  {Read access method for ProductVersionNumber property: fill version info
436
  structure and return it - if there's no version info all values will be zero}
437
begin
438
  Result.V1 := HiWord(fFixedFileInfo.dwProductVersionMS);
439
  Result.V2 := LoWord(fFixedFileInfo.dwProductVersionMS);
440
  Result.V3 := HiWord(fFixedFileInfo.dwProductVersionLS);
441
  Result.V4 := LoWord(fFixedFileInfo.dwProductVersionLS);
442
end;
443
 
444
function TPJVersionInfo.GetStringFileInfo(const Name: string): string;
445
  {Read access method for StringFileInfo array property: returns string
446
  associated with given name or empty string if we have no version info}
447
var
448
  CommandBuf: array[0..255] of char;  // buffer to build API call command str
449
  Ptr: Pointer;                       // pointer to result of API call
450
  Len: UINT;                          // length of structure returned from API
451
begin
452
  // Set default failure result to empty string
453
  Result := '';
454
  // Check if we have valid information recorded in info buffer - exit if not
455
  if fHaveInfo then
456
  begin
457
    // Build API call command string for reading string file info:
458
    //   this uses info string + language and character set
459
    StrPCopy(CommandBuf, '\StringFileInfo\' + GetTransStr + '\' + Name);
460
    // Call API to get required string and return it if successful
461
    if VerQueryValue(fPInfoBuffer, CommandBuf, Ptr, Len) then
462
      Result := PChar(Ptr);
463
  end;
464
end;
465
 
466
function TPJVersionInfo.GetStringFileInfoByIdx(Index: Integer): string;
467
  {Read access method for all string file info properties: returns appropriate
468
  string for the given property or empty string if we have no version info}
469
const
470
  cNames: array[0..11] of string =
471
    ('Comments', 'CompanyName', 'FileDescription', 'FileVersion',
472
    'InternalName', 'LegalCopyright', 'LegalTrademarks', 'OriginalFileName',
473
    'PrivateBuild', 'ProductName', 'ProductVersion', 'SpecialBuild');
474
    {names of predefined string file info strings}
475
begin
476
  Result := GetStringFileInfo(cNames[Index]);
477
end;
478
 
479
procedure TPJVersionInfo.GetTransBuffer(Len: UINT);
480
  {Creates a translation table buffer of required size}
481
begin
482
  // Clear any existing buffer
483
  if fPTransBuffer <> nil then
484
    FreeMem(fPTransBuffer);
485
  // Create the new one
486
  GetMem(fPTransBuffer, Len);
487
end;
488
 
489
function TPJVersionInfo.GetTransStr: string;
490
  {Translation info encoded in string: takes into account current translation}
491
var
492
  TransRec: TTransRec;  // translation record in array of translations
493
begin
494
  if GetCurrentTranslation >= 0 then
495
  begin
496
    // There is a valid current translation: return hex string related to it
497
    TransRec := PTransRecs(fPTransBuffer)^[GetCurrentTranslation];
498
    Result := Format('%4.4x%4.4x', [TransRec.Lang, TransRec.CharSet]);
499
  end
500
  else
501
    // No valid translation string: return empty string
502
    Result := '';
503
end;
504
 
505
procedure TPJVersionInfo.ReadVersionInfo;
506
  {Reads version info from file}
507
var
508
  Len: UINT;        // length of structs returned from API calls
509
  Ptr: Pointer;     // points to version info structures
510
  InfoSize: DWORD;  // size of info buffer
511
  Dummy: DWORD;     // stores 0 in call to GetFileVersionInfoSize
512
begin
513
  // Record default value of HaveInfo property - no info read
514
  fHaveInfo := False;
515
  // Store zeros in fixed file info structure: this is used when no info
516
  FillChar(fFixedFileInfo, SizeOf(fFixedFileInfo), 0);
517
  // Set NumTranslations property to 0: this is value if no info
518
  fNumTranslations := 0;
519
  // Record required size of version info buffer
520
  InfoSize := GetFileVersionInfoSize(PChar(fFileName), Dummy);
521
  // Check that there was no error
522
  if InfoSize > 0 then
523
  begin
524
    // Found info size OK
525
    // Ensure we have a sufficiently large buffer allocated
526
    GetInfoBuffer(InfoSize);
527
    // Read file version info into storage and check success
528
    if GetFileVersionInfo(PChar(fFileName), Dummy, InfoSize, fPInfoBuffer) then
529
    begin
530
      // Success: we've read file version info to storage OK
531
      fHaveInfo := True;
532
      // Get fixed file info & copy to own storage
533
      VerQueryValue(fPInfoBuffer, '\', Ptr, Len);
534
      fFixedFileInfo := PVSFixedFileInfo(Ptr)^;
535
      // Get first translation table info from API
536
      VerQueryValue(fPInfoBuffer, '\VarFileInfo\Translation', Ptr, Len);
537
      // Ptr is to block of translation records each of size Len:
538
      // work out number of translations
539
      fNumTranslations := Len div SizeOf(TTransRec);
540
      // store translation array in a buffer
541
      GetTransBuffer(Len);
542
      Move(Ptr^, fPTransBuffer^, Len);
543
      // make first translation in block current one (-1 if no translations)
544
      SetCurrentTranslation(0);   // adjusts value to -1 if no translations
545
    end;
546
  end;
547
end;
548
 
549
procedure TPJVersionInfo.SetCurrentTranslation(const Value: Integer);
550
  {Write accees method for CurrentTranslation property: if value is out of
551
  translation range then it is set to -1 to indicate no translation}
552
begin
553
  if (Value >= 0) and (Value < NumTranslations) then
554
    fCurrentTranslation := Value
555
  else
556
    fCurrentTranslation := -1
557
end;
558
 
559
procedure TPJVersionInfo.SetFileName(AName: string);
560
  {Write access method for FileName property: action at design time is different
561
  to run time. At design time we simply record value while at run time we store
562
  value - using actual name of program for '' string - and read the version
563
  information}
564
begin
565
  if csDesigning in ComponentState then
566
    // We are designing, simply record the required name
567
    fFileName := AName
568
  else
569
  begin
570
    // It's run-time
571
    // use Application exec file name if name is ''
572
    if AName = '' then
573
      fFileName := ParamStr(0)
574
    else
575
      fFileName := AName;
576
    // clear all properties and read file version info for new file
577
    ClearProperties;
578
    ReadVersionInfo;
579
  end;
580
end;
581
 
582
end.