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

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

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

对于版本号,我使用:

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

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

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

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


当前回答

我刚刚添加了预构建事件命令:

powershell -Command Get-Date -Format 'yyyy-MM-ddTHH:mm:sszzz' > Resources\BuildDateTime.txt

在项目属性中生成一个易于从代码中读取的资源文件。

其他回答

我采纳了阿卜杜勒拉希姆的建议。然而,它似乎给出了一个奇怪的时间格式,还添加了一天的缩写作为构建日期的一部分;例:太阳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);

好消息是,它起作用了。

对于。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();

您可以使用项目构建后事件将文本文件写入具有当前日期时间的目标目录。然后可以在运行时读取值。这有点俗气,但应该有用。

对于. net Core项目,我修改了Postlagerkarte的答案,用构建日期更新程序集版权字段。

直接编辑csproj

以下内容可以直接添加到csproj中的第一个PropertyGroup:

<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>

替代方案:Visual Studio项目属性

或者直接将内部表达式粘贴到Visual Studio中项目属性Package部分的Copyright字段中:

Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))

这可能有点令人困惑,因为Visual Studio将计算表达式并在窗口中显示当前值,但它也将在幕后适当地更新项目文件。

通过Directory.Build.props实现解决方案范围

您可以将上面的<Copyright>元素放入解决方案根目录下的directory . build .props文件中,并将其自动应用于该目录中的所有项目,假设每个项目不提供自己的Copyright值。

<Project>
 <PropertyGroup>
   <Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>
 </PropertyGroup>
</Project>

props:自定义构建

输出

示例表达式将为您提供如下版权:

Copyright © 2018 Travis Troyer (2018-05-30T14:46:23)

检索

你可以在Windows中从文件属性中查看版权信息,或者在运行时获取它:

var version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location);

Console.WriteLine(version.LegalCopyright);

上面的方法可以对进程中已经加载的程序集进行调整,使用内存中的文件映像(而不是从存储中重新读取它):

using System;
using System.Runtime.InteropServices;
using Assembly = System.Reflection.Assembly;

static class Utils
{
    public static DateTime GetLinkerDateTime(this Assembly assembly, TimeZoneInfo tzi = null)
    {
        // Constants related to the Windows PE file format.
        const int PE_HEADER_OFFSET = 60;
        const int LINKER_TIMESTAMP_OFFSET = 8;

        // Discover the base memory address where our assembly is loaded
        var entryModule = assembly.ManifestModule;
        var hMod = Marshal.GetHINSTANCE(entryModule);
        if (hMod == IntPtr.Zero - 1) throw new Exception("Failed to get HINSTANCE.");

        // Read the linker timestamp
        var offset = Marshal.ReadInt32(hMod, PE_HEADER_OFFSET);
        var secondsSince1970 = Marshal.ReadInt32(hMod, offset + LINKER_TIMESTAMP_OFFSET);

        // Convert the timestamp to a DateTime
        var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        var linkTimeUtc = epoch.AddSeconds(secondsSince1970);
        var dt = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tzi ?? TimeZoneInfo.Local);
        return dt;
    }
}