我目前有一个应用程序显示构建号在其标题窗口。这很好,除了它对大多数用户没有任何意义,他们想知道他们是否有最新的版本-他们倾向于将其称为“上周四的”而不是1.0.8.4321版本。

计划是把构建日期放在那里,例如“App构建于21/10/2009”。

我正在努力寻找一种程序化的方法,将构建日期作为文本字符串提取出来,以便像这样使用。

对于版本号,我使用:

Assembly.GetExecutingAssembly().GetName().Version.ToString()

在定义了这些是怎么来的之后。

我希望编译日期(和时间,为了加分)也像这样。

非常感谢这里的指示(如果合适的话,借口双关语),或者更整洁的解决方案……


当前回答

Visual Studio 2019的完整解决方案,就像我几年前开始时希望找到的那样。

添加一个文本资源文件

Access the properties of your project: from the solution explorer, select your project, then right-click -> properties, or Alt+Enter. In the Resources tab, choose Files (Ctrl+5). Then Add Resource / Add New Text File. In the popup message, type the name of your resource, for example BuildDate: this will create a new text file BuildDate.txt in your Project/Resources folder, include it as Project file, and register it as a resource, which can then be accessed via Properties.Resources in C#, or My.Resources in VB.

每次构建时自动更新资源文件

现在,您可以告诉Visual Studio在每次构建或重新构建项目时将日期写入该文件。为此,转到项目属性的编译选项卡,选择生成事件,并复制/粘贴以下内容到“预生成事件命令行”文本框:

powershell -Command "((Get-Date).ToUniversalTime()).ToString(\"s\") | Out-File '$(ProjectDir)Resources\BuildDate.txt'"

这一行将定位BuildDate.txt,并按照ISO8601格式写入today/NowUtc的日期和时间,例如2021-09-07T16:08:35

通过读取文件在运行时获得构建日期

然后你可以在运行时通过下面的助手(c#)从你的代码中检索这个日期:

DateTime CurrentBuildDate = DateTime.Parse(Properties.Resources.BuildDate, null, System.Globalization.DateTimeStyles.RoundtripKind);

学分

基本思路:https://stackoverflow.com/a/15502932/10794555 通过powershell改进:https://stackoverflow.com/users/84898/dbruning ISO8601的稳定解析:如何从ISO8601格式创建。net DateTime

其他回答

将以下内容添加到预构建事件命令行:

echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"

添加这个文件作为资源,现在你有'BuildDate'字符串在你的资源。

在将文件插入资源(作为公共文本文件)后,我通过

string strCompTime = Properties.Resources.BuildDate;

若要创建资源,请参见如何在. net中创建和使用资源。

对于。net 5,我已经成功地使用了这种方法。(在这里找到)。

把这个添加到.csproj文件中:

<SourceRevisionId>build$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmss"))</SourceRevisionId>

获取构建日期的方法:

private static DateTime GetBuildDate(Assembly assembly)
{
    const string BuildVersionMetadataPrefix = "+build";

    var attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
    if (attribute?.InformationalVersion != null)
    {
        var value = attribute.InformationalVersion;
        var index = value.IndexOf(BuildVersionMetadataPrefix);
        if (index > 0)
        {
            value = value.Substring(index + BuildVersionMetadataPrefix.Length);
            if (DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
            {
                return result;
            }
        }
    }

    return default;
}

用法:

 var buildTime = GetBuildDate(Assembly.GetExecutingAssembly());
 buildTime = buildTime.ToLocalTime();

我采纳了阿卜杜勒拉希姆的建议。然而,它似乎给出了一个奇怪的时间格式,还添加了一天的缩写作为构建日期的一部分;例:太阳12/24/2017 13:21:05.43。我只需要日期,所以我必须消除其余使用子字符串。

在添加echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"到预构建事件后,我只做了以下工作:

string strBuildDate = YourNamespace.Properties.Resources.BuildDate;
string strTrimBuildDate = strBuildDate.Substring(4).Remove(10);

好消息是,它起作用了。

在2018年,上面的一些解决方案不再工作,或者不能与。net Core一起工作。

我使用下面的方法,它很简单,适用于我的。net Core 2.0项目。

将以下内容添加到PropertyGroup中的.csproj中:

    <Today>$([System.DateTime]::Now)</Today>

这定义了一个PropertyFunction,您可以在预构建命令中访问它。

您的预构建看起来像这样

echo $(today) > $(ProjectDir)BuildTimeStamp.txt

将BuildTimeStamp.txt的属性设置为嵌入式资源。

现在你可以像这样读时间戳

public static class BuildTimeStamp
    {
        public static string GetTimestamp()
        {
            var assembly = Assembly.GetEntryAssembly(); 

            var stream = assembly.GetManifestResourceStream("NamespaceGoesHere.BuildTimeStamp.txt");

            using (var reader = new StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }
    }

Jeff Atwood在《艰难地确定构建日期》中谈到了这个问题。

最可靠的方法是从可执行文件中嵌入的PE头中检索链接器时间戳——一些c#代码(由Joe Spivey编写)来自Jeff文章的注释:

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

注意:这个方法适用于。net Core 1.0,但在。net Core 1.1之后就不再适用了——它给出的年份是1900-2020年之间的随机值。