如何获取装配体构建日期

问题描述

如何将构建数据添加到程序集中并在关于 ASP.NET 5 MVC 应用程序的“关于”框中显示用户

在 .NET 4.7 MVC 应用程序中使用

//http://www.geekproject.com/showtopic.aspx?ID=21
// When you specify that you want to have buildnumber and revision automatically generated then //the compiler (C#) will generate buildnumber as the number of days from the 1st of January //2000. The revision is generated as the number of seconds from midnight divided by two,but it //will NOT take daylight savings time into account.
[assembly: AssemblyVersion("1.0.*")]

public static DateTime BuildDate
{
    get
    {
        string version = Assembly.GetExecutingAssembly().FullName.Split(',')[1].Trim();
        DateTime start = new DateTime(2000,1,1);
        int buildNumber = Convert.ToInt32(version.Split('.')[2]);
        int revision = Convert.ToInt32(version.Split('.')[3]);
        return start.Add(new TimeSpan(buildNumber,2 * revision,0));
    }
}

在 .NET 5 AssemblyVersion 中抛出

必须关闭确定性和重复属性编译错误

Visual Studio 2019 IDE 用于公共 MVC 应用程序。

解决方法

我使用这个功能很多年了。通用解决方案是使用互操作函数检索和解析始终存在的 VersionInfo 块。你的字符串解析是正确的;如果 Split 返回的项目少于 4 个或者它们不是数字,我会添加错误处理。

最初,日期没有考虑夏令时,但在 Visual Studios 2008 和 2017 之间,微软添加了这一点。我不知道什么时候会发生这种情况。

直到微软没有破坏向后兼容性:

DateTime buildDate = new DateTime(2000,1,1).ToUniversalTime().
    AddDays(build).AddSeconds(revision * 2).ToLocalTime();

对于 Visual Studio 2017(可能更早)及更高版本:

DateTime buildDate = new DateTime(2000,1).
    AddDays(build).AddSeconds(revision * 2));

我试图删除我能删除的所有内容,只搜索“Assebmly 版本”。代码示例仍然相当大,对此感到抱歉。请注意,根据区域设置,带有字符串的块 ("StringFileInfo") 可能会出现多次。

public string GetAssemblyVersion()
{
    string versionString = "";
    IntPtr temp;
    string fileSpec = System.Reflection.Assembly.GetExecutingAssembly().Location;
    uint size = GetFileVersionInfoSizeW(fileSpec,out temp);
    if (size > 0)
    {
        IntPtr ptr = Marshal.AllocHGlobal((int)size);
        if (GetFileVersionInfoW(fileSpec,out temp,size,ptr) != 0)
        {
            versionString = ScanVersionInfo(ptr,"Assembly Version");
        }
        Marshal.FreeHGlobal(ptr);
    }
    return versionString;
}

private string ScanVersionInfo(IntPtr ptr,string key)
{
    int size = Marshal.ReadInt16(ptr);
    byte[] buf = new byte[size];
    Marshal.Copy(ptr,buf,size);

    int fixedFileInfoSize = BitConverter.ToInt16(buf,2);
    int fixedFileInfoOffset = pad32(2 + 2 + 2 + ("VS_VERSION_INFO".Length + 1) * 2);
    int offset = pad32(fixedFileInfoOffset + fixedFileInfoSize);

    return FindStringValue(buf,offset,key);
}

private string FindStringValue(byte[] buf,int offset,string key)
{
    while (offset < buf.Length)
    {

        int wLength = BitConverter.ToUInt16(buf,offset);           // item length with padding
        string blockName = ASCIIEncoding.Unicode.GetString(buf,offset + 6,"StringFileInfo".Length * 2);
        if (blockName == "StringFileInfo")
        {
            int offs = pad32(offset + 2 + 2 + 2 + ("StringFileInfo".Length + 1) * 2);
            string unicodeKey = ASCIIEncoding.Unicode.GetString(buf,offs + 2 + 2 + 2,"12345678".Length * 2);
            int itemOffset = pad32(offs + 2 + 2 + 2 + (unicodeKey.Length + 1) * 2);
            int endOffset = BitConverter.ToInt16(buf,offs) + offs;

            while (itemOffset < endOffset)
            {
                string value;
                if (extractString(buf,ref itemOffset,out value) == key)
                {
                    return value;
                }
            }
        }
        offset = pad32(offset + wLength);                           // advance to the next item
    }
    return "";
}

private string extractString(byte[] buf,ref int offset,out string value)
{
    int wLength      = BitConverter.ToUInt16(buf,offset);      // item length with padding
    int wValueLength = BitConverter.ToInt16(buf,offset + 2);   // value length in words
            
    int start = offset + 2 + 2 + 2;                             // key starts here
    int size = wLength - 2 - 2 - 2 - wValueLength * 2;          // tentative key length in bytes
    string key = ASCIIEncoding.Unicode.GetString(buf,start,size); // extract key
    key = key.Substring(0,key.IndexOf('\0'));                  // clean key's tail

    start = pad32(start + (key.Length + 1) * 2);                // value starts here
    size = (wValueLength - 1) * 2;                              // exact value length in bytes
    value = ASCIIEncoding.Unicode.GetString(buf,size);   // extract value

    offset = pad32(offset + wLength);                           // advance to the next item
    return key;
}

private int pad32(int size)
{
    return ((size + 3) / 4) * 4;
}

[DllImport("version.dll",CharSet = CharSet.Unicode)]
private static extern uint GetFileVersionInfoSizeW(string lptstrFilename,out IntPtr lpdwHandle);

[DllImport("version.dll",CharSet = CharSet.Unicode)]
private static extern uint GetFileVersionInfoW(string lptstrFilename,out IntPtr lpdwHandle,uint dwLen,IntPtr lpData);