簡體   English   中英

如何使用VerQueryValue函數在C#中獲取構建日期?

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

我想在c#中使用VerQueryValue。 但是我不知道該怎么用。 例如:這部分使用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

我想在exe或dll文件中編寫C#代碼以獲取構建日期。

您可以使用一些拼字游戲...

[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;

參考此任務

 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();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM