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

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

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

对于版本号,我使用:

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

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

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

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


当前回答

对于任何需要在Windows 8 / Windows Phone 8中获得编译时间的人:

    public static async Task<DateTimeOffset?> RetrieveLinkerTimestamp(Assembly assembly)
    {
        var pkg = Windows.ApplicationModel.Package.Current;
        if (null == pkg)
        {
            return null;
        }

        var assemblyFile = await pkg.InstalledLocation.GetFileAsync(assembly.ManifestModule.Name);
        if (null == assemblyFile)
        {
            return null;
        }

        using (var stream = await assemblyFile.OpenSequentialReadAsync())
        {
            using (var reader = new DataReader(stream))
            {
                const int PeHeaderOffset = 60;
                const int LinkerTimestampOffset = 8;

                //read first 2048 bytes from the assembly file.
                byte[] b = new byte[2048];
                await reader.LoadAsync((uint)b.Length);
                reader.ReadBytes(b);
                reader.DetachStream();

                //get the pe header offset
                int i = System.BitConverter.ToInt32(b, PeHeaderOffset);

                //read the linker timestamp from the PE header
                int secondsSince1970 = System.BitConverter.ToInt32(b, i + LinkerTimestampOffset);

                var dt = new DateTimeOffset(1970, 1, 1, 0, 0, 0, DateTimeOffset.Now.Offset) + DateTimeOffset.Now.Offset;
                return dt.AddSeconds(secondsSince1970);
            }
        }
    }

对于任何需要在Windows Phone 7中获得编译时间的人:

    public static async Task<DateTimeOffset?> RetrieveLinkerTimestampAsync(Assembly assembly)
    {
        const int PeHeaderOffset = 60;
        const int LinkerTimestampOffset = 8;            
        byte[] b = new byte[2048];

        try
        {
            var rs = Application.GetResourceStream(new Uri(assembly.ManifestModule.Name, UriKind.Relative));
            using (var s = rs.Stream)
            {
                var asyncResult = s.BeginRead(b, 0, b.Length, null, null);
                int bytesRead = await Task.Factory.FromAsync<int>(asyncResult, s.EndRead);
            }
        }
        catch (System.IO.IOException)
        {
            return null;
        }

        int i = System.BitConverter.ToInt32(b, PeHeaderOffset);
        int secondsSince1970 = System.BitConverter.ToInt32(b, i + LinkerTimestampOffset);
        var dt = new DateTimeOffset(1970, 1, 1, 0, 0, 0, DateTimeOffset.Now.Offset) + DateTimeOffset.Now.Offset;
        dt = dt.AddSeconds(secondsSince1970);
        return dt;
    }

注意:在所有情况下,你都运行在沙箱中,所以你只能获得你部署应用程序的程序集的编译时间。(也就是说,这对GAC中的任何东西都无效)。

其他回答

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

如果这是一个windows应用程序,你可以只使用应用程序的可执行路径: 新System.IO.FileInfo (Application.ExecutablePath) .LastWriteTime.ToString(“yyyy.MM.dd”)

这里有很多很棒的答案,但我觉得我可以添加我自己的,因为简单,性能(与资源相关的解决方案相比)跨平台(也适用于Net Core)和避免任何第三方工具。只需将msbuild目标添加到csproj。

<Target Name="Date" BeforeTargets="BeforeBuild">
    <WriteLinesToFile File="$(IntermediateOutputPath)gen.cs" Lines="static partial class Builtin { public static long CompileTime = $([System.DateTime]::UtcNow.Ticks) %3B }" Overwrite="true" />
    <ItemGroup>
        <Compile Include="$(IntermediateOutputPath)gen.cs" />
    </ItemGroup>
</Target>

现在你有了内置。编译时间在这个项目中,例如:

var compileTime = new DateTime(Builtin.CompileTime, DateTimeKind.Utc);

ReSharper不会喜欢的。你可以忽略他,也可以向项目中添加一个partial类,但无论如何都可以工作。

UPD:现在ReSharper在选项的第一页有一个选项:“MSBuild访问”,“每次编译后从MSBuild获取数据”。这有助于生成代码的可见性。

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

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