500 lines
14 KiB
ObjectPascal

{
* FmVIDemo.pas
*
* Main form for Version Information Component VIDemo demo program.
*
* $Rev: 1109 $
* $Date: 2013-01-13 23:19:20 +0000 (Sun, 13 Jan 2013) $
*
* Any copyright in this file is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
}
unit FmVIDemo;
{$UNDEF Supports_RTLNameSpaces}
{$IFDEF CONDITIONALEXPRESSIONS}
{$IF CompilerVersion >= 15.0} // >= Delphi 7
{$WARN UNSAFE_CODE OFF}
{$IFEND}
{$IF CompilerVersion >= 23.0} // Delphi XE2
{$DEFINE Supports_RTLNameSpaces}
{$IFEND}
{$ENDIF}
interface
uses
// Delphi
{$IFDEF Supports_RTLNameSpaces}
Vcl.ComCtrls, Vcl.StdCtrls, Vcl.Buttons, Vcl.Dialogs, Vcl.Controls,
System.Classes, Vcl.Forms, Winapi.Windows, Winapi.Messages, Vcl.ExtCtrls,
{$ELSE}
ComCtrls, StdCtrls, Buttons, Dialogs, Controls, Classes, Forms, Windows,
Messages, ExtCtrls,
{$ENDIF}
// DelphiDabbler component
PJVersionInfo;
type
TMainForm = class(TForm)
dlgBrowse: TOpenDialog;
gpFixed: TGroupBox;
lvFixed: TListView;
gpVar: TGroupBox;
lblTrans: TLabel;
lblStr: TLabel;
cmbTrans: TComboBox;
lvStr: TListView;
bvlSpacer1: TBevel;
viInfo: TPJVersionInfo;
sbFileName: TSpeedButton;
edFileName: TEdit;
btnRefresh: TButton;
btnClose: TButton;
sbHints: TStatusBar;
procedure cmbTransChange(Sender: TObject);
procedure lvMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnRefreshClick(Sender: TObject);
procedure sbFileNameClick(Sender: TObject);
procedure btnCloseClick(Sender: TObject);
private
procedure ClearDisplay;
// Clear list views and combo box
procedure Display;
// Display version info for current file, if any
procedure DisplayFFI;
// Display fixed file info for current file
procedure DisplayFFIItem(const Index: Integer; const FFI: TVSFixedFileInfo);
// Display the fixed file item per the given index fixed file info
// structure
procedure DisplayTransInfo;
// Display translation information in combo and selects first item if any
procedure DisplayStringInfo(const TransIdx: Integer);
// Display standard string information for given translation
procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
// Catch name of file dropped on window and enter in edit control
end;
var
MainForm: TMainForm;
implementation
uses
// Delphi
{$IFDEF Supports_RTLNameSpaces}
System.SysUtils, System.Math, WinApi.ShellAPI;
{$ELSE}
SysUtils, Math, ShellAPI;
{$ENDIF}
{$R *.DFM}
const
// Index of fixed file info in lvFixed
cIdxFileVersion = 0;
cIdxProductVersion = 1;
cIdxFileFlagsMask = 2;
cIdxFileFlags = 3;
cIdxFileOS = 4;
cIdxFileType = 5;
cIdxFileSubType = 6;
cFirstFFIIndex = 0;
cLastFFIIndex = 6;
// The following types and constant table definitions provide information
// required to display descriptions of fixed file information codes from version
// resources
type
TTableEntry = record
// maps codes to descriptions
Code: DWORD;
Desc: string;
end;
const
cFileType: array[0..6] of TTableEntry =
// maps file types to descriptions
(
(Code: VFT_APP; Desc: 'Application'),
(Code: VFT_DLL; Desc: 'DLL'),
(Code: VFT_DRV; Desc: 'Device driver'),
(Code: VFT_FONT; Desc: 'Font'),
(Code: VFT_STATIC_LIB; Desc: 'Static link library'),
(Code: VFT_VXD; Desc: 'Virtual device driver'),
(Code: VFT_UNKNOWN; Desc: 'Unknown')
);
cFileOSBase: array[0..4] of TTableEntry =
// maps base OS codes to descriptions
(
( Code: VOS_NT; Desc: 'Windows NT' ),
( Code: VOS_DOS; Desc: 'MS-DOS' ),
( Code: VOS_OS232; Desc: 'OS2 32 bit' ),
( Code: VOS_OS216; Desc: 'OS2 16 bit' ),
( Code: VOS_UNKNOWN; Desc: 'Any' )
);
cFileOSTarget: array[0..4] of TTableEntry =
// maps target OS codes to descriptions
(
( Code: VOS__WINDOWS32; Desc: '32 bit Windows' ),
( Code: VOS__WINDOWS16; Desc: 'Windows 3.x' ),
( Code: VOS__PM32; Desc: 'Presentation Manager 32' ),
( Code: VOS__PM16; Desc: 'Presentation Manager 16' ),
( Code: VOS__BASE; Desc: 'Unknown' )
);
// The following procedures extract the required descriptions of fixed file
// information codes from the tables above
function CodeToDesc(Code: DWORD;
Table: array of TTableEntry): string;
// Return description of given code using given table
var
I: Integer;
begin
Result := '';
for I := Low(Table) to High(Table) do
if Table[I].Code = Code then
begin
Result := Table[I].Desc;
Break;
end;
end;
function FileOSDesc(OS: DWORD): string;
// Describe OS
var
Target, Base: DWORD;
begin
// get target and base OS
Target := OS and $0000FFFF;
Base := OS and $FFFF0000;
// build description
if Base = VOS_UNKNOWN then
Result := CodeToDesc(Target, cFileOSTarget)
else if Target = VOS__BASE then
Result := CodeToDesc(Base, cFileOSBase)
else
Result := Format('%s on %s',
[CodeToDesc(Target, cFileOSTarget),
CodeToDesc(Base, cFileOSBase)]);
end;
function FileFlagsToStr(Flags: DWORD): string;
// Return string of file flags from given bit set
const
cFileFlags: array[0..5] of TTableEntry =
(
(Code: VS_FF_DEBUG; Desc: 'Debug'),
(Code: VS_FF_PRERELEASE; Desc: 'Pre-release'),
(Code: VS_FF_PATCHED; Desc: 'Patched'),
(Code: VS_FF_PRIVATEBUILD; Desc: 'Private build'),
(Code: VS_FF_INFOINFERRED; Desc: 'Inferred'),
(Code: VS_FF_SPECIALBUILD; Desc: 'Special build') );
var
I: Integer;
begin
Result := '';
for I := Low(cFileFlags) to High(cFileFlags) do
if Flags and cFileFlags[I].Code = cFileFlags[I].Code then
if Result = '' then
Result := cFileFlags[I].Desc
else
Result := Result + ', ' + cFileFlags[I].Desc
end;
function VerFmt(const MS, LS: DWORD): string;
// Format the version number from the given DWORDs containing the info
begin
Result := Format('%d.%d.%d.%d',
[HiWord(MS), LoWord(MS), HiWord(LS), LoWord(LS)])
end;
{ TMainForm }
procedure TMainForm.btnCloseClick(Sender: TObject);
// Close the app
begin
Close;
end;
procedure TMainForm.btnRefreshClick(Sender: TObject);
// Display version info for file name entered in edit control
begin
// Store name of required file in version info component: this reads ver info
viInfo.FileName := edFileName.Text;
// Record file's name in caption
Caption := Format('%s - %s',
[Application.Title, ExtractFileName(viInfo.FileName)]);
// Display info, if any
Display
end;
procedure TMainForm.ClearDisplay;
// Clear list views and combo box
// ---------------------------------------------------------------------------
procedure ClearLV(LV: TListView);
var
Idx: Integer;
begin
for Idx := 0 to Pred(LV.Items.Count) do
if LV.Items[Idx].SubItems.Count > 0 then
LV.Items[Idx].SubItems[0] := '';
end;
// ---------------------------------------------------------------------------
begin
ClearLV(lvFixed);
ClearLV(lvStr);
cmbTrans.Items.Clear;
cmbTrans.ItemIndex := -1;
end;
procedure TMainForm.cmbTransChange(Sender: TObject);
// When user selects a translation from combo box, display its string info
begin
DisplayStringInfo(cmbTrans.ItemIndex);
end;
procedure TMainForm.Display;
// Display version info for current file, if any
begin
// Display version info: display gets cleared and message displayed if no info
if viInfo.HaveInfo then
begin
DisplayFFI;
DisplayTransInfo;
DisplayStringInfo(cmbTrans.ItemIndex);
end
else
begin
ClearDisplay;
MessageDlg(
'No version resource information available for ' + viInfo.FileName,
mtInformation, [mbOK], 0);
end;
end;
procedure TMainForm.DisplayFFI;
// Display fixed file info for current file
var
Idx: Integer; // scans thru all fixed file items
begin
// Display each fixed file item
for Idx := cFirstFFIIndex to cLastFFIIndex do
begin
// Ensure there's a sub item in list view for this item
if lvFixed.Items[Idx].SubItems.Count = 0 then
lvFixed.Items[Idx].SubItems.Add('');
// Display the item
DisplayFFIItem(Idx, viInfo.FixedFileInfo);
end;
end;
procedure TMainForm.DisplayFFIItem(const Index: Integer;
const FFI: TVSFixedFileInfo);
// Display the fixed file item per the given index fixed file info structure
// ---------------------------------------------------------------------------
procedure AddItem(const Index: Integer; const Value: string);
{Set the first sub item of the given list item to the given value}
begin
lvFixed.Items[Index].SubItems[0] := Value;
end;
// ---------------------------------------------------------------------------
begin
// Display the required item
case Index of
cIdxFileVersion:
AddItem(cIdxFileVersion,
VerFmt(FFI.dwFileVersionMS, FFI.dwFileVersionLS));
cIdxProductVersion:
AddItem(cIdxProductVersion,
VerFmt(FFI.dwProductVersionMS, FFI.dwProductVersionLS));
cIdxFileFlagsMask:
AddItem(cIdxFileFlagsMask,
FileFlagsToStr(FFI.dwFileFlagsMask));
cIdxFileFlags:
AddItem(cIdxFileFlags, FileFlagsToStr(FFI.dwFileFlags));
cIdxFileOS:
AddItem(cIdxFileOS, FileOSDesc(FFI.dwFileOS));
cIdxFileType:
AddItem(cIdxFileType, CodeToDesc(FFI.dwFileType, cFileType));
cIdxFileSubType:
case FFI.dwFileType of
VFT_FONT, VFT_DRV, VFT_VXD:
AddItem(cIdxFileSubType, Format('%0.8X', [FFI.dwFileSubType]));
else AddItem(cIdxFileSubType, 'None');
end;
end;
end;
procedure TMainForm.DisplayStringInfo(const TransIdx: Integer);
// Display standard string information for given translation
const
// Details of names of all standard string information
cStrInfoNames: array[0..11] of string = (
'Comments',
'CompanyName',
'FileDescription',
'FileVersion',
'InternalName',
'LegalCopyright',
'LegalTrademarks',
'OriginalFileName',
'PrivateBuild',
'ProductName',
'ProductVersion',
'SpecialBuild'
);
var
Idx: Integer; // index of string info name in table
begin
// Select required translation in version info component
viInfo.CurrentTranslation := TransIdx;
// Display each piece of string info
for Idx := Low(cStrInfoNames) to High(cStrInfoNames) do
begin
// Ensure we have a sub-item of list item in which to display info
if lvStr.Items[Idx].SubItems.Count = 0 then
lvStr.Items[Idx].SubItems.Add('');
// Display string info or empty string if translation is not valid
if viInfo.CurrentTranslation > -1 then
// display string information
lvStr.Items[Idx].SubItems[0] :=
viInfo.StringFileInfo[cStrInfoNames[Idx]]
else
// clear string information
lvStr.Items[Idx].SubItems[0] := '';
end;
end;
procedure TMainForm.DisplayTransInfo;
// Display translation information in combo and selects first item if any
var
TransIdx: Integer; // loops thru all translations
begin
// Set up translations in combo box: indexes of items in combo = trans index
cmbTrans.Clear;
for TransIdx := 0 to Pred(viInfo.NumTranslations) do
begin
viInfo.CurrentTranslation := TransIdx;
cmbTrans.Items.Add(Format('%s - %s', [viInfo.Language, viInfo.CharSet]));
end;
// Select first translation if there is one
if viInfo.NumTranslations > 0 then
cmbTrans.ItemIndex := 0
else
cmbTrans.ItemIndex := -1;
end;
procedure TMainForm.FormCreate(Sender: TObject);
// App starting: initialise (tell Windows we accept file drag/drops)
begin
DragAcceptFiles(Handle, True);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
// App closing: tidy up (no more file drag/drops)
begin
DragAcceptFiles(Handle, False);
end;
procedure TMainForm.lvMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
// Display any info that is too wide for list view column in pop-up window
// when mouse passes over the item
const
{$J+}
LastItem: TListItem = nil; // list item under cursor last time event called
{$J-}
var
LV: TListView; // list view triggering this event
Item: TListItem; // list item, if any, under mouse cursor
StrW: Integer; // width of string in right column for current list item
// ---------------------------------------------------------------------------
procedure SetHint(const HintStr: string);
{Set list view's hint to given string, and switch on hinting if string non-
empty}
begin
LV.Hint := HintStr;
LV.ShowHint := HintStr <> '';
sbHints.AutoHint := HintStr = ''; // only auto hint for interactive controls
end;
// ---------------------------------------------------------------------------
begin
// Get reference to list view triggering this event
LV := Sender as TListView;
// Get list item under mouse cursor if any
Item := LV.GetItemAt(X, Y);
if Item <> LastItem then
begin
// Item has changed: ensure any active hint is cancelled so any new one will
// be displayed
LastItem := Item;
Application.CancelHint;
end;
if Assigned(Item)
and (X > LV.Columns[0].WidthType)
and (Item.SubItems.Count > 0) then
begin
// Mouse cursor is over right column of a valid list item
// .. we display narative as hint only if narrative is wider than column
StrW := LV.StringWidth(Item.SubItems[0]);
if StrW > LV.Columns[1].WidthType - 12 then
SetHint(Item.SubItems[0])
else
SetHint('');
end
else
// Mouse is not over an area containing narative text: no hint
SetHint('');
end;
procedure TMainForm.sbFileNameClick(Sender: TObject);
// Get a file name from uer and display in edit box
begin
if dlgBrowse.Execute then
edFileName.Text := dlgBrowse.FileName;
end;
procedure TMainForm.WMDropFiles(var Msg: TWMDropFiles);
// Catch name of file dropped on window and enter in edit control
var
NumDropped: Integer; // no of files dropped
NameLen: Integer; // length of a file name
FileName: string; // name of a dropped file
begin
inherited;
// Find number of files dropped
NumDropped := DragQueryFile(Msg.Drop, Cardinal(-1), nil, 0);
try
if NumDropped > 0 then
begin
// Find size required for filename buffer (without terminal #0)
NameLen := DragQueryFile(Msg.Drop, 0, nil, 0);
// Get name of dropped file: only interested in first if more than 1
SetLength(FileName, NameLen); // Delphi adds space for terminal #0
DragQueryFile(Msg.Drop, 0, PChar(FileName), NameLen + 1);
// Place name of file in edit control
edFileName.Text := FileName;
end;
finally
// Release handle assoc. with drag/drop
DragFinish(Msg.Drop);
end;
end;
end.