我目前有一个应用程序显示构建号在其标题窗口。这很好,除了它对大多数用户没有任何意义,他们想知道他们是否有最新的版本-他们倾向于将其称为“上周四的”而不是1.0.8.4321版本。
计划是把构建日期放在那里,例如“App构建于21/10/2009”。
我正在努力寻找一种程序化的方法,将构建日期作为文本字符串提取出来,以便像这样使用。
对于版本号,我使用:
Assembly.GetExecutingAssembly().GetName().Version.ToString()
在定义了这些是怎么来的之后。
我希望编译日期(和时间,为了加分)也像这样。
非常感谢这里的指示(如果合适的话,借口双关语),或者更整洁的解决方案……
上面的方法可以对进程中已经加载的程序集进行调整,使用内存中的文件映像(而不是从存储中重新读取它):
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;
}
}
关于从程序集PE头的字节中提取构建日期/版本信息的技术,Microsoft已经从Visual Studio 15.4开始更改了默认的构建参数。新的默认包含确定性编译,这使得有效的时间戳和自动递增的版本号成为过去式。时间戳字段仍然存在,但它被一个永久值填充,该值是某个东西的哈希值,而不是任何构建时间的指示。
这里有一些详细的背景
对于那些将有用的时间戳优先于确定性编译的人来说,有一种方法可以覆盖新的默认值。您可以在感兴趣的程序集的.csproj文件中包含一个标记,如下所示:
<PropertyGroup>
...
<Deterministic>false</Deterministic>
</PropertyGroup>
Update:
I endorse the T4 text template solution described in another answer here. I used it to solve my issue cleanly without losing the benefit of deterministic compilation. One caution about it is that Visual Studio only runs the T4 compiler when the .tt file is saved, not at build time. This can be awkward if you exclude the .cs result from source control (since you expect it to be generated) and another developer checks out the code. Without resaving, they won't have the .cs file. There is a package on nuget (I think called AutoT4) that makes T4 compilation part of every build. I have not yet confronted the solution to this during production deployment, but I expect something similar to make it right.
对于. 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);