Subversion Repositories spacemission

Compare Revisions

Problem with comparison.

Regard whitespace Rev HEAD → Rev 1

/VCL_PJVERSIONINFO/PJVersionInfo.pas
0,0 → 1,582
{ ##
@FILE PJVersionInfo.pas
@COMMENTS Version Information Component (32 bit) source code
(development split from 16 bit version after v1.0).
@PROJECT_NAME Version Information Component
@PROJECT_DESC Component that reads version information from files.
@OTHER_NAMES + Original unit name was VerInfo.pas
+ Changed to VInfo.pas at v2.1
+ Changed to PJVersionInfo.pas at v3.0
@AUTHOR Peter Johnson, LLANARTH, Ceredigion, Wales, UK
@EMAIL peter.johnson@openlink.org
@WEBSITE http://www.pjsoft.contactbox.co.uk/
@COPYRIGHT © 1998-2002, Peter D Johnson.
@LEGAL_NOTICE This component is placed in the public domain. It
may be freely copied and circulated on a not for
profit basis providing that the code is unmodified
and this notice and information about the author and
his copyright remains attached to the source code.
@CREDITS In producing this component some techniques were
used which were learned from FVersion by PJ Veger,
Best, The Netherlands (Feb/96). In particular the
method of accessing language and char-set tables was
taken from PJ Veger's code.
@HISTORY(
@REVISION(
@VERSION 1.0
@DATE 25/04/1998
@COMMENTS Original version - 16 bit only.
)
@REVISION(
@VERSION 2.0
@DATE 06/12/1998
@COMMENTS Revised for use with 32 bit Windows. Not compatible
with v1.
)
@REVISION(
@VERSION 2.0.1
@DATE 09/04/1999
@COMMENTS Changed installation palette from "Own" to "PJ
Stuff".
)
@REVISION(
@VERSION 2.1
@DATE 28/11/1999
@COMMENTS + Changed unit name to VInfo from VerInfo to allow
component to install under Delphi 3 & 4 (VerInfo
clashes with an existing unit in these versions).
+ Removed superfluous conditional compilation
directives.
)
@REVISION(
@VERSION 3.0
@DATE 17/02/2002
@COMMENTS Major update:
+ Added ability to access all "translations" stored
in a file's version information, rather than just
first one. This has been done so that code using
earlier versions of this component should continue
to work unchanged.
+ Added new property to expose fixed file
information record.
+ Added new "string array" property to give access
to string information by name: this property can
access any custom string information if the name
is known.
+ Added properties to return number of
"translations" and to select index of
"translation" to be used.
+ Added properites to return language and character
set codes in addition to descriptive strings.
+ All string info, language and character set
properties now return values from the currently
selected translation (which defaults to the first
translation maintaining backward compatibilty).
+ Empty file name string now accesses name of host
application per command line rather than using
Application.ExeName. This enables this code to
work correctly even if user changes name of
executable file.
+ CharSet property now returns '' for unknown value
rather than 'Unknown'
+ Renamed unit to PJVersionInfo from VInfo.
+ Renamed TVersionNumber record to TPJVersionNumber
+ Replaced Tvs_FixedFileInfo record with use of
Windows unit defined type TVSFixedFileInfo.
+ Changed component palette from PJ Stuff to PJSoft
)
)
}
 
 
unit PJVersionInfo;
 
interface
 
uses
// Delphi
Windows, Classes;
 
type
 
{
TPJVersionNumber:
Record holding version numbers.
}
TPJVersionNumber = record
V1, V2, V3, V4: WORD;
end;
 
{
TPJVersionInfo:
Component that exposes the version information embedded in an executable
file and exposed the detail as properties.
}
TPJVersionInfo = class(TComponent)
private // properties
fFileName: string;
fHaveInfo: Boolean;
fNumTranslations: Integer;
fCurrentTranslation: Integer;
fFixedFileInfo: TVSFixedFileInfo;
procedure SetFileName(AName: string);
function GetProductVersionNumber: TPJVersionNumber;
function GetFileVersionNumber: TPJVersionNumber;
function GetLanguage: string;
function GetCharSet: string;
function GetCharSetCode: WORD;
function GetLanguageCode: WORD;
function GetCurrentTranslation: Integer;
procedure SetCurrentTranslation(const Value: Integer);
function GetStringFileInfo(const Name: string): string;
function GetStringFileInfoByIdx(Index: Integer): string;
private
fPInfoBuffer: PChar;
{Pointer to info buffer}
fPTransBuffer: Pointer;
{Pointer to translation buffer}
procedure GetInfoBuffer(Len: DWORD);
{Creates an info buffer of required size}
procedure GetTransBuffer(Len: UINT);
{Creates a translation table buffer of required size}
function GetTransStr: string;
{Translation info encoded in string: takes into account current
translation}
protected
procedure ClearProperties; virtual;
{Forces properties to return cleared values}
procedure ReadVersionInfo; virtual;
{Reads version info from file}
public
constructor Create(AOwner: TComponent); override;
{Class constructor: sets default values}
destructor Destroy; override;
{Class destructor: frees allocated memory}
{Properties}
property HaveInfo: Boolean
read fHaveInfo;
{Property true if file version info for the file per FileName property has
been successfully read}
property FixedFileInfo: TVSFixedFileInfo
read fFixedFileInfo;
{Exposes the whole fixed file info record: following properties expose
the various fields of it}
property FileVersionNumber: TPJVersionNumber read
GetFileVersionNumber;
{Version number of file, in numeric format, from fixed file info}
property ProductVersionNumber: TPJVersionNumber
read GetProductVersionNumber;
{Version number of product, in numeric format, from fixed file info}
property FileOS: DWORD
read fFixedFileInfo.dwFileOS;
{Code describing operating system to be used by file}
property FileType: DWORD
read fFixedFileInfo.dwFileType;
{Code descibing type of file}
property FileSubType: DWORD
read fFixedFileInfo.dwFileSubType;
{Code describing sub-type of file - only used for certain values of
FileType property}
property FileFlagsMask: DWORD
read fFixedFileInfo.dwFileFlagsMask;
{Code describing which FileFlags are valid}
property FileFlags: DWORD
read fFixedFileInfo.dwFileFlags;
{Flags describing file state}
property Comments: string index 0
read GetStringFileInfoByIdx;
{String file info property giving user defined comments for current
translation}
property CompanyName: string index 1
read GetStringFileInfoByIdx;
{String file info property giving name of company for current translation}
property FileDescription: string index 2
read GetStringFileInfoByIdx;
{String file info property giving description of file for current
translation}
property FileVersion: string index 3
read GetStringFileInfoByIdx;
{String file info property giving version number of file in string format
for current translation}
property InternalName: string index 4
read GetStringFileInfoByIdx;
{String file info property giving internal name of file for current
translation}
property LegalCopyright: string index 5
read GetStringFileInfoByIdx;
{String file info property giving copyright message for current
translation}
property LegalTrademarks: string index 6
read GetStringFileInfoByIdx;
{String file info property giving trademark info for current translation}
property OriginalFileName: string index 7
read GetStringFileInfoByIdx;
{String file info property giving original name of file for current
translation}
property PrivateBuild: string index 8
read GetStringFileInfoByIdx;
{String file info property giving information about a private build of
file for current translation}
property ProductName: string index 9
read GetStringFileInfoByIdx;
{String file info property giving name of product for current translation}
property ProductVersion: string index 10
read GetStringFileInfoByIdx;
{String file info property giving version number of product in string
format for current translation}
property SpecialBuild: string index 11
read GetStringFileInfoByIdx;
{String file info property giving information about a special build of
file for current translation}
property StringFileInfo[const Name: string]: string
read GetStringFileInfo;
{Returns the value for string file info with given name for current
translation. This property can return both standard and custom string
info}
property Language: string
read GetLanguage;
{Name of language in use in current translation}
property CharSet: string
read GetCharSet;
{Name of character set in use in current translation}
property LanguageCode: WORD
read GetLanguageCode;
{Code of laguage in use in current translation}
property CharSetCode: WORD
read GetCharSetCode;
{Character set code in use in current translation}
property NumTranslations: Integer
read fNumTranslations;
{The number of difference translations (ie languages and char sets) in
the version information}
property CurrentTranslation: Integer
read GetCurrentTranslation write SetCurrentTranslation;
{Zero-based index of the current translation: this is 0 when a file is
first accessed. Set to a value in range 0..NumTranslations-1 to access
other translations. All string info, language and char set properties
return information for the current translation}
published
property FileName: string read fFileName write SetFileName;
{Name of file to which version information relates}
end;
 
procedure Register;
{Register this component}
 
implementation
 
uses
// Delphi
SysUtils;
 
{ Component registration routine }
 
procedure Register;
{Register this component}
begin
RegisterComponents('PJSoft', [TPJVersionInfo]);
end;
 
{ TPJVersionInfo }
 
type
{
TTransRec:
Record of language code and char set codes that are returned from version
information.
}
TTransRec = packed record // record to hold translation information
Lang, CharSet: Word;
end;
{
TTransRecs:
Type used to type cast translation data into an array of translation
records.
}
TTransRecs = array[0..1000] of TTransRec;
{
PTransRecs:
Pointer to an array of translation records.
}
PTransRecs = ^TTransRecs;
 
procedure TPJVersionInfo.ClearProperties;
{Forces properties to return cleared values}
begin
// Record that we haven't read ver info: this effectively clears properties
// since each property read access method checks this flag before returning
// result
fHaveInfo := False;
end;
 
constructor TPJVersionInfo.Create(AOwner: TComponent);
{Class constructor: sets default values}
begin
inherited Create(AOwner);
// Default is no file name - refers to executable file for application
FileName := '';
end;
 
destructor TPJVersionInfo.Destroy;
{Class destructor: frees allocated memory}
begin
// Ensure that info buffer is freed if allocated
if fPInfoBuffer <> nil then
StrDispose(fPInfoBuffer);
// Ensure that translation buffer is free if allocated
if fPTransBuffer <> nil then
FreeMem(fPTransBuffer);
inherited Destroy;
end;
 
function TPJVersionInfo.GetCharSet: string;
{Read access method for CharSet property: return string describing character
set if we have some version info and empty string if we haven't}
const
// Map code numbers to char-set names
cCharSets: array[0..11] of record
Code: Word; // char set code
Str: string; // associated name of char set
end = (
( Code: 0; Str: '7-bit ASCII' ),
( Code: 932; Str: 'Windows, Japan (Shift - JIS X-0208)'),
( Code: 949; Str: 'Windows, Korea (Shift - KSC 5601)' ),
( Code: 950; Str: 'Windows, Taiwan (GB5)' ),
( Code: 1200; Str: 'Unicode' ),
( Code: 1250; Str: 'Windows, Latin-2 (Eastern European)'),
( Code: 1251; Str: 'Windows, Cyrillic' ),
( Code: 1252; Str: 'Windows, Multilingual' ),
( Code: 1253; Str: 'Windows, Greek' ),
( Code: 1254; Str: 'Windows, Turkish' ),
( Code: 1255; Str: 'Windows, Hebrew' ),
( Code: 1256; Str: 'Windows, Arabic' )
);
var
I: Integer; // loop control
begin
Result := '';
if fHaveInfo then
begin
// We've have ver info: scan table of codes looking for required entry
for I := Low(cCharSets) to High(cCharSets) do
if GetCharSetCode = cCharSets[I].Code then
begin
// found one - record its name
Result := cCharSets[I].Str;
Exit;
end;
end;
end;
 
function TPJVersionInfo.GetCharSetCode: WORD;
{Read access for CharSetCode property: returns char set code for current
translation or 0 if there is no translation or we have no version info}
begin
if fHaveInfo and (GetCurrentTranslation >= 0) then
Result := PTransRecs(fPTransBuffer)^[GetCurrentTranslation].CharSet
else
Result := 0;
end;
 
function TPJVersionInfo.GetCurrentTranslation: Integer;
{Read access method for CurrentTranslation property: returns index to current
translation if we have version info or -1 if no version info}
begin
if fHaveInfo then
Result := fCurrentTranslation
else
Result := -1;
end;
 
function TPJVersionInfo.GetFileVersionNumber: TPJVersionNumber;
{Read access method for FileVersionNumber property: fill version info
structure and return it - if there's no version info all values will be zero}
begin
Result.V1 := HiWord(fFixedFileInfo.dwFileVersionMS);
Result.V2 := LoWord(fFixedFileInfo.dwFileVersionMS);
Result.V3 := HiWord(fFixedFileInfo.dwFileVersionLS);
Result.V4 := LoWord(fFixedFileInfo.dwFileVersionLS);
end;
 
procedure TPJVersionInfo.GetInfoBuffer(Len: DWORD);
{Creates an info buffer of required size}
begin
// Clear any existing buffer
if fPInfoBuffer <> nil then
StrDispose(fPInfoBuffer);
// Create the new one
fPInfoBuffer := StrAlloc(Len);
end;
 
function TPJVersionInfo.GetLanguage: string;
{Read access method for Language property: return string describing language
if we have some version info and empty string if we haven't}
var
Buf: array[0..255] of char; // stores langauge string from API call
begin
// Assume failure
Result := '';
// Try to get language name from Win API if we have ver info
if fHaveInfo and (VerLanguageName(GetLanguageCode, Buf, 255) > 0) then
Result := Buf;
end;
 
function TPJVersionInfo.GetLanguageCode: WORD;
{Read access for LanguageCode property: returns language code for current
translation or 0 if there is no translation or we have no version info}
begin
if fHaveInfo and (GetCurrentTranslation >= 0) then
Result := PTransRecs(fPTransBuffer)^[GetCurrentTranslation].Lang
else
Result := 0;
end;
 
function TPJVersionInfo.GetProductVersionNumber: TPJVersionNumber;
{Read access method for ProductVersionNumber property: fill version info
structure and return it - if there's no version info all values will be zero}
begin
Result.V1 := HiWord(fFixedFileInfo.dwProductVersionMS);
Result.V2 := LoWord(fFixedFileInfo.dwProductVersionMS);
Result.V3 := HiWord(fFixedFileInfo.dwProductVersionLS);
Result.V4 := LoWord(fFixedFileInfo.dwProductVersionLS);
end;
 
function TPJVersionInfo.GetStringFileInfo(const Name: string): string;
{Read access method for StringFileInfo array property: returns string
associated with given name or empty string if we have no version info}
var
CommandBuf: array[0..255] of char; // buffer to build API call command str
Ptr: Pointer; // pointer to result of API call
Len: UINT; // length of structure returned from API
begin
// Set default failure result to empty string
Result := '';
// Check if we have valid information recorded in info buffer - exit if not
if fHaveInfo then
begin
// Build API call command string for reading string file info:
// this uses info string + language and character set
StrPCopy(CommandBuf, '\StringFileInfo\' + GetTransStr + '\' + Name);
// Call API to get required string and return it if successful
if VerQueryValue(fPInfoBuffer, CommandBuf, Ptr, Len) then
Result := PChar(Ptr);
end;
end;
 
function TPJVersionInfo.GetStringFileInfoByIdx(Index: Integer): string;
{Read access method for all string file info properties: returns appropriate
string for the given property or empty string if we have no version info}
const
cNames: array[0..11] of string =
('Comments', 'CompanyName', 'FileDescription', 'FileVersion',
'InternalName', 'LegalCopyright', 'LegalTrademarks', 'OriginalFileName',
'PrivateBuild', 'ProductName', 'ProductVersion', 'SpecialBuild');
{names of predefined string file info strings}
begin
Result := GetStringFileInfo(cNames[Index]);
end;
 
procedure TPJVersionInfo.GetTransBuffer(Len: UINT);
{Creates a translation table buffer of required size}
begin
// Clear any existing buffer
if fPTransBuffer <> nil then
FreeMem(fPTransBuffer);
// Create the new one
GetMem(fPTransBuffer, Len);
end;
 
function TPJVersionInfo.GetTransStr: string;
{Translation info encoded in string: takes into account current translation}
var
TransRec: TTransRec; // translation record in array of translations
begin
if GetCurrentTranslation >= 0 then
begin
// There is a valid current translation: return hex string related to it
TransRec := PTransRecs(fPTransBuffer)^[GetCurrentTranslation];
Result := Format('%4.4x%4.4x', [TransRec.Lang, TransRec.CharSet]);
end
else
// No valid translation string: return empty string
Result := '';
end;
 
procedure TPJVersionInfo.ReadVersionInfo;
{Reads version info from file}
var
Len: UINT; // length of structs returned from API calls
Ptr: Pointer; // points to version info structures
InfoSize: DWORD; // size of info buffer
Dummy: DWORD; // stores 0 in call to GetFileVersionInfoSize
begin
// Record default value of HaveInfo property - no info read
fHaveInfo := False;
// Store zeros in fixed file info structure: this is used when no info
FillChar(fFixedFileInfo, SizeOf(fFixedFileInfo), 0);
// Set NumTranslations property to 0: this is value if no info
fNumTranslations := 0;
// Record required size of version info buffer
InfoSize := GetFileVersionInfoSize(PChar(fFileName), Dummy);
// Check that there was no error
if InfoSize > 0 then
begin
// Found info size OK
// Ensure we have a sufficiently large buffer allocated
GetInfoBuffer(InfoSize);
// Read file version info into storage and check success
if GetFileVersionInfo(PChar(fFileName), Dummy, InfoSize, fPInfoBuffer) then
begin
// Success: we've read file version info to storage OK
fHaveInfo := True;
// Get fixed file info & copy to own storage
VerQueryValue(fPInfoBuffer, '\', Ptr, Len);
fFixedFileInfo := PVSFixedFileInfo(Ptr)^;
// Get first translation table info from API
VerQueryValue(fPInfoBuffer, '\VarFileInfo\Translation', Ptr, Len);
// Ptr is to block of translation records each of size Len:
// work out number of translations
fNumTranslations := Len div SizeOf(TTransRec);
// store translation array in a buffer
GetTransBuffer(Len);
Move(Ptr^, fPTransBuffer^, Len);
// make first translation in block current one (-1 if no translations)
SetCurrentTranslation(0); // adjusts value to -1 if no translations
end;
end;
end;
 
procedure TPJVersionInfo.SetCurrentTranslation(const Value: Integer);
{Write accees method for CurrentTranslation property: if value is out of
translation range then it is set to -1 to indicate no translation}
begin
if (Value >= 0) and (Value < NumTranslations) then
fCurrentTranslation := Value
else
fCurrentTranslation := -1
end;
 
procedure TPJVersionInfo.SetFileName(AName: string);
{Write access method for FileName property: action at design time is different
to run time. At design time we simply record value while at run time we store
value - using actual name of program for '' string - and read the version
information}
begin
if csDesigning in ComponentState then
// We are designing, simply record the required name
fFileName := AName
else
begin
// It's run-time
// use Application exec file name if name is ''
if AName = '' then
fFileName := ParamStr(0)
else
fFileName := AName;
// clear all properties and read file version info for new file
ClearProperties;
ReadVersionInfo;
end;
end;
 
end.