如何在c#中使用VerQueryValue函数获取构建日期?

时间:2017-04-05 08:44:25

标签: c# c++

我想在c#中使用VerQueryValue。但我不知道我该怎么用。 例如:这部分使用c ++程序(exe。或dll)编写了嵌入式构建日期。所以我必须得到这个构建日期

//##############
//# Javascript #
//##############

class NoteController{
  constructor() {
    this.store = new NoteStore(); // instance of a dataStore
  }

  HandleInputFields(){ // Enable / Disable a button by validation
    var input = document.getElementById('edtNoteTitle').value; // Get the input text from the field
    var inputIsValid = true;

    if(input.length < 1) // text is empty
        inputIsValid = false;
    else if (this.store.notes.some(n => n.title === input)) // check for duplicates in the store
        inputIsValid = false;

    document.getElementById('btnCreateNote').disabled = !inputIsValid; // disable the button or keep it enabled
  }
}

//########
//# HTML #
//########

<body onload="HandleInputFields()"> // Disable the button when loading the Document

<input type="text" id="edtNoteTitle" onchange="HandleInputFields()"> // Validate the Input field

</body>

我想在exe或dll文件中为get build date编写c#代码。

2 个答案:

答案 0 :(得分:3)

你可以使用一些pinvokes ......

[DllImport("version.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetFileVersionInfoSize(string lptstrFilename, out int lpdwHandle);

[DllImport("version.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetFileVersionInfo(string lptstrFilename, int dwHandle, int dwLen, byte[] lpData);

[DllImport("version.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool VerQueryValue(byte[] pBlock, string lpSubBlock, out IntPtr lplpBuffer, out int puLen);

public static Tuple<string, string>[] GetVersionInfo(string fileName, params string[] keys)
{
    int num;
    int size = GetFileVersionInfoSize(fileName, out num);

    if (size == 0)
    {
        throw new Win32Exception();
    }

    var bytes = new byte[size];
    bool success = GetFileVersionInfo(fileName, 0, size, bytes);

    if (!success)
    {
        throw new Win32Exception();
    }

    int size2;
    IntPtr ptr;

    success = VerQueryValue(bytes, @"\VarFileInfo\Translation", out ptr, out size2);

    uint[] langs;

    if (success)
    {
        langs = new uint[size2 / 4];

        for (int i = 0, j = 0; j < size2; i++, j += 4)
        {
            langs[i] = unchecked((uint)(((ushort)Marshal.ReadInt16(ptr, j) << 16) | (ushort)Marshal.ReadInt16(ptr, j + 2)));
        }
    }
    else
    {
        // Taken from https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/FileVersionInfo.cs,470
        langs = new uint[] { 0x040904B0, 0x040904E4, 0x04090000 };
    }

    string[] langs2 = Array.ConvertAll(langs, x => @"\StringFileInfo\" + x.ToString("X8") + @"\");

    var kv = new Tuple<string, string>[keys.Length];

    for (int i = 0; i < kv.Length; i++)
    {
        string key = keys[i];
        string value = null;

        foreach (var lang in langs2)
        {
            success = VerQueryValue(bytes, lang + key, out ptr, out size2);

            if (success)
            {
                value = Marshal.PtrToStringUni(ptr);
                break;
            }
        }

        kv[i] = Tuple.Create(key, value);
    }

    return kv;
}

然后你使用:

string name = "Win32Project1.exe";
var infos = GetVersionInfo(name, "LastModified", "Comments", "CompanyName", "FileVersion", "LegalCopyright", "LegalTrademarks", "ProductVersion", "InternalName", "OriginalFilename", "FileDescription", "ProductName", "BuildDate");

var buildDate = infos.Single(x => x.Item1 == "BuildDate").Item2;

出于好奇,我开始探索VS_VERSIONINFO的各种结构,并且我已经编写了一些代码:

public class VersionInfo
{
    [DllImport("version.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetFileVersionInfoSize(string lptstrFilename, out int lpdwHandle);

    [DllImport("version.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool GetFileVersionInfo(string lptstrFilename, int dwHandle, int dwLen, byte[] lpData);

    [DllImport("version.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool VerQueryValue(byte[] pBlock, string lpSubBlock, out IntPtr lplpBuffer, out int puLen);

    public readonly Version FileVersion;
    public readonly Version ProductVersion;
    public readonly uint FileFlagsMask;
    public readonly uint FileFlags;
    public readonly uint FileOS;
    public readonly uint FileType;
    public readonly uint FileSubtype;
    // Always null
    public readonly DateTime? FileDate;

    protected VersionInfo(Version fileVersion, Version productVersion, uint fileFlagsMask, uint fileFlags, uint fileOS, uint fileType, uint fileSubtype, DateTime? fileDate)
    {
        FileVersion = fileVersion;
        ProductVersion = productVersion;
        FileFlagsMask = fileFlagsMask;
        FileFlags = fileFlags;
        FileOS = fileOS;
        FileType = fileType;
        FileSubtype = fileSubtype;
        FileDate = fileDate;
    }

    // vi can be null on exit
    // Item1 = language | codepage
    // Item2 = Key
    // Item3 = Value
    public static IEnumerable<Tuple<uint, string, string>> ReadVersionInfo(string fileName, out VersionInfo vi)
    {
        int num;
        int size = GetFileVersionInfoSize(fileName, out num);

        if (size == 0)
        {
            throw new Win32Exception();
        }

        var buffer = new byte[size];
        bool success = GetFileVersionInfo(fileName, 0, size, buffer);

        if (!success)
        {
            throw new Win32Exception();
        }

        return ReadVersionInfo(buffer, out vi);

    }

    // vi can be null on exit
    // Item1 = language | codepage
    // Item2 = Key
    // Item3 = Value
    public static IEnumerable<Tuple<uint, string, string>> ReadVersionInfo(byte[] buffer, out VersionInfo vi)
    {
        int offset;
        // The offset calculated here is unused
        var fibs = ReadFileInfoBaseStruct(buffer, 0, out offset);

        if (fibs.Key != "VS_VERSION_INFO")
        {
            throw new Exception(fibs.Key);
        }

        // Value = VS_FIXEDFILEINFO
        if (fibs.ValueLength != 0)
        {
            uint signature = BitConverter.ToUInt32(buffer, fibs.ValueOffset);

            if (signature != 0xFEEF04BD)
            {
                throw new Exception(signature.ToString("X8"));
            }

            uint strucVersion = BitConverter.ToUInt32(buffer, fibs.ValueOffset + 4);

            var fileVersion = new Version(BitConverter.ToUInt16(buffer, fibs.ValueOffset + 10), BitConverter.ToUInt16(buffer, fibs.ValueOffset + 8), BitConverter.ToUInt16(buffer, fibs.ValueOffset + 14), BitConverter.ToUInt16(buffer, fibs.ValueOffset + 12));
            var productVersion = new Version(BitConverter.ToUInt16(buffer, fibs.ValueOffset + 18), BitConverter.ToUInt16(buffer, fibs.ValueOffset + 16), BitConverter.ToUInt16(buffer, fibs.ValueOffset + 22), BitConverter.ToUInt16(buffer, fibs.ValueOffset + 20));

            uint fileFlagsMask = BitConverter.ToUInt32(buffer, fibs.ValueOffset + 24);
            uint fileFlags = BitConverter.ToUInt32(buffer, fibs.ValueOffset + 28);
            uint fileOS = BitConverter.ToUInt32(buffer, fibs.ValueOffset + 32);
            uint fileType = BitConverter.ToUInt32(buffer, fibs.ValueOffset + 36);
            uint fileSubtype = BitConverter.ToUInt32(buffer, fibs.ValueOffset + 40);

            uint fileDateMS = BitConverter.ToUInt32(buffer, fibs.ValueOffset + 44);
            uint fileDateLS = BitConverter.ToUInt32(buffer, fibs.ValueOffset + 48);
            DateTime? fileDate = fileDateMS != 0 || fileDateLS != 0 ?
                (DateTime?)DateTime.FromFileTime((long)fileDateMS << 32 | fileDateLS) :
                null;

            vi = new VersionInfo(fileVersion, productVersion, fileFlagsMask, fileFlags, fileOS, fileType, fileSubtype, fileDate);
        }
        else
        {
            vi = null;
        }

        return ReadVersionInfoInternal(buffer, fibs);
    }

    protected static IEnumerable<Tuple<uint, string, string>> ReadVersionInfoInternal(byte[] buffer, FileInfoBaseStruct fibs)
    {
        int sfiOrValOffset = (fibs.ValueOffset + fibs.ValueLength + 3) & (~3);

        while (sfiOrValOffset < fibs.Length)
        {
            int nextSfiOrValOffset;

            var sfiOrVal = ReadFileInfoBaseStruct(buffer, sfiOrValOffset, out nextSfiOrValOffset);

            if (sfiOrVal.Key == "StringFileInfo")
            {
                int stOffset = sfiOrVal.ValueOffset;

                while (stOffset < sfiOrVal.EndOffset)
                {
                    int nextStOffset;

                    var st = ReadFileInfoBaseStruct(buffer, stOffset, out nextStOffset);

                    uint langCharset = uint.Parse(st.Key, NumberStyles.HexNumber);

                    int striOffset = st.ValueOffset;

                    while (striOffset < st.EndOffset)
                    {
                        int nextStriOffset;

                        var stri = ReadFileInfoBaseStruct(buffer, striOffset, out nextStriOffset);

                        // Here stri.ValueLength is in words!
                        int len = FindLengthUnicodeSZ(buffer, stri.ValueOffset, stri.ValueOffset + (stri.ValueLength * 2));
                        string value = Encoding.Unicode.GetString(buffer, stri.ValueOffset, len * 2);

                        yield return Tuple.Create(langCharset, stri.Key, value);

                        striOffset = nextStriOffset;
                    }

                    stOffset = nextStOffset;
                }
            }
            else if (sfiOrVal.Key == "VarFileInfo")
            {
                int varOffset = sfiOrVal.ValueOffset;

                while (varOffset < sfiOrVal.EndOffset)
                {
                    int nextVarOffset;

                    var var = ReadFileInfoBaseStruct(buffer, varOffset, out nextVarOffset);

                    if (var.Key != "Translation")
                    {
                        throw new Exception(var.Key);
                    }

                    int langOffset = var.ValueOffset;

                    while (langOffset < var.EndOffset)
                    {
                        unchecked
                        {
                            // We invert the order suggested by the Var description!
                            uint high = (uint)BitConverter.ToInt16(buffer, langOffset);
                            uint low = (uint)BitConverter.ToInt16(buffer, langOffset + 2);
                            uint lang = (high << 16) | low;

                            langOffset += 4;
                        }
                    }

                    varOffset = nextVarOffset;
                }
            }
            else
            {
                Debug.WriteLine("Unrecognized " + sfiOrVal.Key);
            }

            sfiOrValOffset = nextSfiOrValOffset;
        }
    }

    protected static FileInfoBaseStruct ReadFileInfoBaseStruct(byte[] buffer, int offset, out int nextOffset)
    {
        var fibs = new FileInfoBaseStruct
        {
            Length = BitConverter.ToInt16(buffer, offset),
            ValueLength = BitConverter.ToInt16(buffer, offset + 2),
            Type = BitConverter.ToInt16(buffer, offset + 4)
        };

        int len = FindLengthUnicodeSZ(buffer, offset + 6, offset + fibs.Length);
        fibs.Key = Encoding.Unicode.GetString(buffer, offset + 6, len * 2);

        // Padding
        fibs.ValueOffset = ((offset + 6 + (len + 1) * 2) + 3) & (~3);

        fibs.EndOffset = offset + fibs.Length;
        nextOffset = (fibs.EndOffset + 3) & (~3);

        return fibs;
    }

    protected static int FindLengthUnicodeSZ(byte[] buffer, int offset, int endOffset)
    {
        int offset2 = offset;
        while (offset2 < endOffset && BitConverter.ToInt16(buffer, offset2) != 0)
        {
            offset2 += 2;
        }

        // In chars
        return (offset2 - offset) / 2;
    }

    // Used internally
    protected class FileInfoBaseStruct
    {
        public short Length { get; set; }
        public short ValueLength { get; set; }
        public short Type { get; set; }
        public string Key { get; set; }
        public int ValueOffset { get; set; }
        public int EndOffset { get; set; }
    }
}

使用它像:

string name = "Win32Project1-loc.exe";

// vi could be null on return from ReadVersionInfo
VersionInfo vi;

// Note that it is an IEnumerable<>... If you want to use 
// it multipel times, you should .ToArray() it!
var infos = VersionInfo.ReadVersionInfo(name, out vi);

// For example
var buildDate = infos.Single(x => x.Item2 == "BuildDate").Item3;

答案 1 :(得分:0)

参考this queston

 public static DateTime GetLinkerTime(this Assembly assembly, TimeZoneInfo target = null)
    {
        var filePath = assembly.Location;
        const int c_PeHeaderOffset = 60;
        const int c_LinkerTimestampOffset = 8;

        var buffer = new byte[2048];

        using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            stream.Read(buffer, 0, 2048);

        var offset = BitConverter.ToInt32(buffer, c_PeHeaderOffset);
        var secondsSince1970 = BitConverter.ToInt32(buffer, offset + c_LinkerTimestampOffset);
        var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        var linkTimeUtc = epoch.AddSeconds(secondsSince1970);

        var tz = target ?? TimeZoneInfo.Local;
        var localTime = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tz);

        return localTime;
    }

用法示例:

var linkTimeLocal = Assembly.GetExecutingAssembly().GetLinkerTime();