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. |