简体   繁体   English

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

[英]how can I use VerQueryValue function for get build date in c#?

I want to use VerQueryValue in c# . 我想在c#中使用VerQueryValue。 But I dont know how can I use. 但是我不知道该怎么用。 For example : This part wrote using c++ program(exe. or dll) Build date is embeded.So I have to get this build date 例如:这部分使用c ++程序(exe或dll)编写,并且嵌入了构建日期。因此,我必须获取此构建日期

VS_VERSION_INFO VERSIONINFO
FILEVERSION BUILDVRS1,BUILDVRS2,BUILDVRS3,BUILDVRS4
PRODUCTVERSION BUILDVRS1,BUILDVRS2,BUILDVRS3,BUILDVRS0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "LastModified" , "20170313\0"
            VALUE "Comments", "\0"
            VALUE "CompanyName", BUILDCOMPANY
            VALUE "FileVersion", BUILDFILE
            VALUE "LegalCopyright", "Copyright (C) 1999-2002\0"
            VALUE "LegalTrademarks", "\0"
            VALUE "ProductVersion", BUILDPROD
            VALUE "InternalName", "SeriCom\0"
            VALUE "OriginalFilename", "SeriCom.DLL\0"
            VALUE "FileDescription", "SeriCom DLL\0"
            VALUE "ProductName", "SeriCom Dynamic Link Library\0"
            VALUE "BuildMach", BUILDMACH
            VALUE "BuildDate", BUILDDATE
            VALUE "BuildType", BUILDTYPE
            VALUE "BuildVers", BUILDVPRX
            VALUE "BuildNumb", BUILDNUMB
            VALUE "BuildChar", BUILDCHAR
            VALUE "BuildEnv1", BUILDCOMP
            VALUE "BuildEnv2", BUILDMFC
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

I want to write c# code for get build date in exe or dll file. 我想在exe或dll文件中编写C#代码以获取构建日期。

You can use some 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;
}

and then you use: 然后您使用:

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;

Out of curiosity I've began exploring the various structs of the VS_VERSIONINFO , and I've written some code: 出于好奇,我开始探索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; }
    }
}

use it like: 像这样使用它:

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;

Take reference of 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;
    }

Usage example: 用法示例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM