【问题标题】:How to get assembly build date如何获取程序集构建日期
【发布时间】:2021-06-30 06:22:12
【问题描述】:

如何将构建数据添加到程序集并在 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, 0, 0, 2 * revision, 0));
    }
}

在 .NET 5 AssemblyVersion 中抛出

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

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

【问题讨论】:

标签: visual-studio asp.net-core asp.net-core-mvc .net-assembly .net-5


【解决方案1】:

我多年来一直使用此功能。通用解决方案是使用互操作函数检索和解析始终存在的 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, 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, 0, 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, start, 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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-22
    • 2011-01-04
    • 1970-01-01
    • 2015-08-30
    • 1970-01-01
    • 2018-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多