527 lines
21 KiB
C#
527 lines
21 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="FileVersionInfo.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Diagnostics {
|
|
using Microsoft.Win32;
|
|
using System.Runtime.Serialization.Formatters;
|
|
using System.Text;
|
|
using System.Configuration.Assemblies;
|
|
using System.Runtime.InteropServices;
|
|
using System.IO;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
using System;
|
|
using System.Globalization;
|
|
using System.Runtime.Versioning;
|
|
|
|
/// <devdoc>
|
|
/// <para>Provides version information for a physical file on disk.</para>
|
|
/// </devdoc>
|
|
[
|
|
// Disabling partial trust scenarios
|
|
PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")
|
|
]
|
|
public sealed class FileVersionInfo {
|
|
|
|
private string fileName;
|
|
private string companyName;
|
|
private string fileDescription;
|
|
private string fileVersion;
|
|
private string internalName;
|
|
private string legalCopyright;
|
|
private string originalFilename;
|
|
private string productName;
|
|
private string productVersion;
|
|
private string comments;
|
|
private string legalTrademarks;
|
|
private string privateBuild;
|
|
private string specialBuild;
|
|
private string language;
|
|
private int fileMajor;
|
|
private int fileMinor;
|
|
private int fileBuild;
|
|
private int filePrivate;
|
|
private int productMajor;
|
|
private int productMinor;
|
|
private int productBuild;
|
|
private int productPrivate;
|
|
private int fileFlags;
|
|
|
|
private FileVersionInfo(string fileName) {
|
|
this.fileName = fileName;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the comments associated with the file.</para>
|
|
/// </devdoc>
|
|
public string Comments {
|
|
get {
|
|
return comments;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the name of the company that produced the file.</para>
|
|
/// </devdoc>
|
|
public string CompanyName {
|
|
get {
|
|
return companyName;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the build number of the file.</para>
|
|
/// </devdoc>
|
|
public int FileBuildPart {
|
|
get {
|
|
return fileBuild;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the description of the file.</para>
|
|
/// </devdoc>
|
|
public string FileDescription {
|
|
get {
|
|
return fileDescription;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the major part of the version number.</para>
|
|
/// </devdoc>
|
|
public int FileMajorPart {
|
|
get {
|
|
return fileMajor;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the minor
|
|
/// part of the version number of the file.</para>
|
|
/// </devdoc>
|
|
public int FileMinorPart {
|
|
get {
|
|
return fileMinor;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the name of the file that this instance of System.Windows.Forms.FileVersionInfo
|
|
/// describes.</para>
|
|
/// </devdoc>
|
|
public string FileName {
|
|
get {
|
|
new FileIOPermission(FileIOPermissionAccess.PathDiscovery, fileName).Demand();
|
|
return fileName;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the file private part number.</para>
|
|
/// </devdoc>
|
|
public int FilePrivatePart {
|
|
get {
|
|
return filePrivate;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the file version number.</para>
|
|
/// </devdoc>
|
|
public string FileVersion {
|
|
get {
|
|
return fileVersion;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the internal name of the file, if one exists.</para>
|
|
/// </devdoc>
|
|
public string InternalName {
|
|
get {
|
|
return internalName;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets a value that specifies whether the file
|
|
/// contains debugging information or is compiled with debugging features enabled.</para>
|
|
/// </devdoc>
|
|
public bool IsDebug {
|
|
get {
|
|
return (fileFlags & NativeMethods.VS_FF_DEBUG) != 0;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets a value that specifies whether the file has been modified and is not identical to
|
|
/// the original shipping file of the same version number.</para>
|
|
/// </devdoc>
|
|
public bool IsPatched {
|
|
get {
|
|
return (fileFlags & NativeMethods.VS_FF_PATCHED) != 0;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets a value that specifies whether the file was built using standard release procedures.</para>
|
|
/// </devdoc>
|
|
public bool IsPrivateBuild {
|
|
get {
|
|
return (fileFlags & NativeMethods.VS_FF_PRIVATEBUILD) != 0;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets a value that specifies whether the file
|
|
/// is a development version, rather than a commercially released product.</para>
|
|
/// </devdoc>
|
|
public bool IsPreRelease {
|
|
get {
|
|
return (fileFlags & NativeMethods.VS_FF_PRERELEASE) != 0;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets a value that specifies whether the file is a special build.</para>
|
|
/// </devdoc>
|
|
public bool IsSpecialBuild {
|
|
get {
|
|
return (fileFlags & NativeMethods.VS_FF_SPECIALBUILD) != 0;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>
|
|
/// Gets the default language string for the version info block.
|
|
/// </para>
|
|
/// </devdoc>
|
|
public string Language {
|
|
get {
|
|
return language;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets all copyright notices that apply to the specified file.</para>
|
|
/// </devdoc>
|
|
public string LegalCopyright {
|
|
get {
|
|
return legalCopyright;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the trademarks and registered trademarks that apply to the file.</para>
|
|
/// </devdoc>
|
|
public string LegalTrademarks {
|
|
get {
|
|
return legalTrademarks;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the name the file was created with.</para>
|
|
/// </devdoc>
|
|
public string OriginalFilename {
|
|
get {
|
|
return originalFilename;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets information about a private version of the file.</para>
|
|
/// </devdoc>
|
|
public string PrivateBuild {
|
|
get {
|
|
return privateBuild;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the build number of the product this file is associated with.</para>
|
|
/// </devdoc>
|
|
public int ProductBuildPart {
|
|
get {
|
|
return productBuild;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the major part of the version number for the product this file is associated with.</para>
|
|
/// </devdoc>
|
|
public int ProductMajorPart {
|
|
get {
|
|
return productMajor;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the minor part of the version number for the product the file is associated with.</para>
|
|
/// </devdoc>
|
|
public int ProductMinorPart {
|
|
get {
|
|
return productMinor;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the name of the product this file is distributed with.</para>
|
|
/// </devdoc>
|
|
public string ProductName {
|
|
get {
|
|
return productName;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the private part number of the product this file is associated with.</para>
|
|
/// </devdoc>
|
|
public int ProductPrivatePart {
|
|
get {
|
|
return productPrivate;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the version of the product this file is distributed with.</para>
|
|
/// </devdoc>
|
|
public string ProductVersion {
|
|
get {
|
|
return productVersion;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets the special build information for the file.</para>
|
|
/// </devdoc>
|
|
public string SpecialBuild {
|
|
get {
|
|
return specialBuild;
|
|
}
|
|
}
|
|
|
|
private static string ConvertTo8DigitHex(int value) {
|
|
string s = Convert.ToString(value, 16);
|
|
s = s.ToUpper(CultureInfo.InvariantCulture);
|
|
if (s.Length == 8) {
|
|
return s;
|
|
}
|
|
else {
|
|
StringBuilder b = new StringBuilder(8);
|
|
for (int l = s.Length;l<8; l++) {
|
|
b.Append("0");
|
|
}
|
|
b.Append(s);
|
|
return b.ToString();
|
|
}
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
|
|
private static NativeMethods.VS_FIXEDFILEINFO GetFixedFileInfo(IntPtr memPtr) {
|
|
IntPtr memRef = IntPtr.Zero;
|
|
int memLen;
|
|
|
|
if (UnsafeNativeMethods.VerQueryValue(new HandleRef(null, memPtr), "\\", ref memRef, out memLen)) {
|
|
NativeMethods.VS_FIXEDFILEINFO fixedFileInfo = new NativeMethods.VS_FIXEDFILEINFO();
|
|
Marshal.PtrToStructure(memRef, fixedFileInfo);
|
|
return fixedFileInfo;
|
|
}
|
|
|
|
return new NativeMethods.VS_FIXEDFILEINFO();
|
|
}
|
|
|
|
private static string GetFileVersionLanguage( IntPtr memPtr ) {
|
|
int langid = GetVarEntry( memPtr ) >> 16;
|
|
|
|
StringBuilder lang = new StringBuilder( 256 );
|
|
UnsafeNativeMethods.VerLanguageName( langid, lang, lang.Capacity );
|
|
return lang.ToString();
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
|
|
private static string GetFileVersionString(IntPtr memPtr, string name) {
|
|
string data = "";
|
|
|
|
IntPtr memRef = IntPtr.Zero;
|
|
int memLen;
|
|
|
|
if (UnsafeNativeMethods.VerQueryValue(new HandleRef(null, memPtr), name, ref memRef, out memLen)) {
|
|
|
|
if (memRef != IntPtr.Zero) {
|
|
data = Marshal.PtrToStringAuto(memRef);
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
|
|
private static int GetVarEntry(IntPtr memPtr) {
|
|
IntPtr memRef = IntPtr.Zero;
|
|
int memLen;
|
|
|
|
if (UnsafeNativeMethods.VerQueryValue(new HandleRef(null, memPtr), "\\VarFileInfo\\Translation", ref memRef, out memLen)) {
|
|
return(Marshal.ReadInt16(memRef) << 16) + Marshal.ReadInt16((IntPtr)((long)memRef + 2));
|
|
}
|
|
|
|
return 0x040904E4;
|
|
}
|
|
|
|
//
|
|
// This function tries to find version informaiton for a specific codepage.
|
|
// Returns true when version information is found.
|
|
//
|
|
private bool GetVersionInfoForCodePage(IntPtr memIntPtr, string codepage) {
|
|
string template = "\\\\StringFileInfo\\\\{0}\\\\{1}";
|
|
|
|
companyName = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "CompanyName"));
|
|
fileDescription = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "FileDescription"));
|
|
fileVersion = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "FileVersion"));
|
|
internalName = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "InternalName"));
|
|
legalCopyright = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "LegalCopyright"));
|
|
originalFilename = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "OriginalFilename"));
|
|
productName = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "ProductName"));
|
|
productVersion = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "ProductVersion"));
|
|
comments = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "Comments"));
|
|
legalTrademarks = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "LegalTrademarks"));
|
|
privateBuild = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "PrivateBuild"));
|
|
specialBuild = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "SpecialBuild"));
|
|
|
|
language = GetFileVersionLanguage( memIntPtr );
|
|
|
|
NativeMethods.VS_FIXEDFILEINFO ffi = GetFixedFileInfo(memIntPtr);
|
|
fileMajor = HIWORD(ffi.dwFileVersionMS);
|
|
fileMinor = LOWORD(ffi.dwFileVersionMS);
|
|
fileBuild = HIWORD(ffi.dwFileVersionLS);
|
|
filePrivate = LOWORD(ffi.dwFileVersionLS);
|
|
productMajor = HIWORD(ffi.dwProductVersionMS);
|
|
productMinor = LOWORD(ffi.dwProductVersionMS);
|
|
productBuild = HIWORD(ffi.dwProductVersionLS);
|
|
productPrivate = LOWORD(ffi.dwProductVersionLS);
|
|
fileFlags = ffi.dwFileFlags;
|
|
|
|
// fileVersion is chosen based on best guess. Other fields can be used if appropriate.
|
|
return (fileVersion != string.Empty);
|
|
}
|
|
|
|
//
|
|
// Get the full path of fileName using a declarative Assert.
|
|
//
|
|
[ResourceExposure(ResourceScope.Machine)]
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
[FileIOPermission(SecurityAction.Assert, AllFiles=FileIOPermissionAccess.PathDiscovery)]
|
|
static string GetFullPathWithAssert(string fileName) {
|
|
return Path.GetFullPath(fileName);
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Returns a System.Windows.Forms.FileVersionInfo representing the version information associated with the specified file.</para>
|
|
/// </devdoc>
|
|
[ResourceExposure(ResourceScope.Machine)]
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
public unsafe static FileVersionInfo GetVersionInfo(string fileName) {
|
|
// Check for the existence of the file. File.Exists returns false
|
|
// if Read permission is denied.
|
|
if (!File.Exists(fileName)) {
|
|
//
|
|
// The previous version of this code in the success case would require
|
|
// one imperative Assert for PathDiscovery permission, one Demand for
|
|
// PathDiscovery permission (blocked by the Assert), and 2 demands for
|
|
// Read permission. It turns out that File.Exists does a demand for
|
|
// Read permission, so in the success case, we only need to do a single Demand.
|
|
// In the success case, this change increases the performance of this
|
|
// function dramatically.
|
|
//
|
|
// In the failure case, we want to remain backwardly compatible by throwing
|
|
// a SecurityException in the case where Read access is denied
|
|
// (it can be argued that this is less secure than throwing a FileNotFoundException,
|
|
// but perhaps not so much as to be worth a breaking change).
|
|
// File.Exists eats a SecurityException, so we need to Demand for it
|
|
// here. Since performance in the failure case is not crucial, as an
|
|
// exception will be thrown anyway, we do a Demand for Read access.
|
|
// If that does not throw an exception, then we will throw a FileNotFoundException.
|
|
//
|
|
// We also change the code to do a declarative Assert for PathDiscovery,
|
|
// as that performs much better than an imperative Assert.
|
|
//
|
|
string fullPath = GetFullPathWithAssert(fileName);
|
|
new FileIOPermission(FileIOPermissionAccess.Read, fullPath).Demand();
|
|
throw new FileNotFoundException(fileName);
|
|
}
|
|
|
|
int handle; // This variable is not used, but we need an out variable.
|
|
int infoSize = UnsafeNativeMethods.GetFileVersionInfoSize(fileName, out handle);
|
|
FileVersionInfo versionInfo = new FileVersionInfo(fileName);
|
|
|
|
if (infoSize != 0) {
|
|
byte[] mem = new byte[infoSize];
|
|
fixed (byte* memPtr = mem) {
|
|
IntPtr memIntPtr = new IntPtr((void*) memPtr);
|
|
if (UnsafeNativeMethods.GetFileVersionInfo(fileName, 0, infoSize, new HandleRef(null, memIntPtr))) {
|
|
int langid = GetVarEntry(memIntPtr);
|
|
if( !versionInfo.GetVersionInfoForCodePage(memIntPtr, ConvertTo8DigitHex(langid))) {
|
|
// Some dlls might not contain correct codepage information. In this case we will fail during lookup.
|
|
// Explorer will take a few shots in dark by trying following ID:
|
|
//
|
|
// 040904B0 // US English + CP_UNICODE
|
|
// 040904E4 // US English + CP_USASCII
|
|
// 04090000 // US English + unknown codepage
|
|
// Explorer also randomly guess 041D04B0=Swedish+CP_UNICODE and 040704B0=German+CP_UNICODE) sometimes.
|
|
// We will try to simulate similiar behavior here.
|
|
int[] ids = new int[] {0x040904B0, 0x040904E4, 0x04090000};
|
|
foreach( int id in ids) {
|
|
if( id != langid) {
|
|
if(versionInfo.GetVersionInfoForCodePage(memIntPtr, ConvertTo8DigitHex(id))) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return versionInfo;
|
|
}
|
|
|
|
private static int HIWORD(int dword) {
|
|
return NativeMethods.Util.HIWORD(dword);
|
|
}
|
|
|
|
private static int LOWORD(int dword) {
|
|
return NativeMethods.Util.LOWORD(dword);
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Returns a partial list of properties in System.Windows.Forms.FileVersionInfo
|
|
/// and their values.</para>
|
|
/// </devdoc>
|
|
public override string ToString() {
|
|
StringBuilder sb = new StringBuilder(128);
|
|
String nl = "\r\n";
|
|
sb.Append("File: "); sb.Append(FileName); sb.Append(nl);
|
|
sb.Append("InternalName: "); sb.Append(InternalName); sb.Append(nl);
|
|
sb.Append("OriginalFilename: "); sb.Append(OriginalFilename); sb.Append(nl);
|
|
sb.Append("FileVersion: "); sb.Append(FileVersion); sb.Append(nl);
|
|
sb.Append("FileDescription: "); sb.Append(FileDescription); sb.Append(nl);
|
|
sb.Append("Product: "); sb.Append(ProductName); sb.Append(nl);
|
|
sb.Append("ProductVersion: "); sb.Append(ProductVersion); sb.Append(nl);
|
|
sb.Append("Debug: "); sb.Append(IsDebug.ToString()); sb.Append(nl);
|
|
sb.Append("Patched: "); sb.Append(IsPatched.ToString()); sb.Append(nl);
|
|
sb.Append("PreRelease: "); sb.Append(IsPreRelease.ToString()); sb.Append(nl);
|
|
sb.Append("PrivateBuild: "); sb.Append(IsPrivateBuild.ToString()); sb.Append(nl);
|
|
sb.Append("SpecialBuild: "); sb.Append(IsSpecialBuild.ToString()); sb.Append(nl);
|
|
sb.Append("Language: "); sb.Append(Language); sb.Append(nl);
|
|
return sb.ToString();
|
|
}
|
|
|
|
}
|
|
}
|