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