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

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

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

对于版本号,我使用:

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

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

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

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


当前回答

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

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

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

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

string strCompTime = Properties.Resources.BuildDate;

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

其他回答

对于. net Core(。NET 5+),可以这样做。它的优点在于不需要添加或嵌入文件,没有T4,也没有预构建脚本。

在你的项目中添加这样一个类:

namespace SuperDuper
{
    [AttributeUsage(AttributeTargets.Assembly)]
    public class BuildDateTimeAttribute : Attribute
    {
        public string Date { get; set; }
        public BuildDateTimeAttribute(string date)
        {
            Date = date;
        }
    }
}

更新项目的.csproj以包含如下内容:

<ItemGroup>
    <AssemblyAttribute Include="SuperDuper.BuildDateTime">
        <_Parameter1>$([System.DateTime]::Now.ToString("s"))</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

注意,_Parameter1是一个神奇的名字——它意味着BuildDateTime属性类的构造函数的第一个(也是唯一一个)参数。

这就是在程序集中记录构建日期时间所需的全部内容。

然后读取程序集的构建日期时间,执行如下操作:

private static DateTime? getAssemblyBuildDateTime()
{
    var assembly = System.Reflection.Assembly.GetExecutingAssembly();
    var attr = Attribute.GetCustomAttribute(assembly, typeof(BuildDateTimeAttribute)) as BuildDateTimeAttribute;
    if (DateTime.TryParse(attr?.Date, out DateTime dt))
        return dt;
    else
        return null;
}

注1(根据评论中的Flydog57):如果你的.csproj有属性GenerateAssemblyInfo在其中列出并设置为false,构建将不会生成程序集信息,你将不会在程序集中获得BuildDateTime信息。因此,要么不要在.csproj中提到GenerateAssemblyInfo(这是新项目的默认行为,如果没有特别设置为false, GenerateAssemblyInfo默认为true),要么显式地将其设置为true。

注2(根据Teddy在评论中的说法):在给出的_Parameter1示例中,我们使用::Now来使用DateTime。现在,这是您的计算机上的本地日期和时间,如果适用日光节约时间和您的本地时区。如果你想使用::UtcNow来使用DateTime,你可以这样做。UtcNow以便将构建日期和时间记录为UTC/GMT。

另一种不同的pcl友好的方法是使用MSBuild内联任务将构建时间替换为应用程序上的属性返回的字符串。我们在一个具有Xamarin的应用程序中成功地使用了这种方法。形式,Xamarin的。Android和Xamarin。iOS项目。

编辑:

通过将所有逻辑移动到SetBuildDate来简化。目标文件,并使用Regex而不是简单的字符串替换,这样文件可以在每次构建时修改而不“重置”。

MSBuild内联任务定义(保存在SetBuildDate中。目标文件本地到Xamarin。本例中的表单项目):

<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="12.0">

  <UsingTask TaskName="SetBuildDate" TaskFactory="CodeTaskFactory" 
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
    <ParameterGroup>
      <FilePath ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs"><![CDATA[

        DateTime now = DateTime.UtcNow;
        string buildDate = now.ToString("F");
        string replacement = string.Format("BuildDate => \"{0}\"", buildDate);
        string pattern = @"BuildDate => ""([^""]*)""";
        string content = File.ReadAllText(FilePath);
        System.Text.RegularExpressions.Regex rgx = new System.Text.RegularExpressions.Regex(pattern);
        content = rgx.Replace(content, replacement);
        File.WriteAllText(FilePath, content);
        File.SetLastWriteTimeUtc(FilePath, now);

   ]]></Code>
    </Task>
  </UsingTask>

</Project>

在Xamarin中调用上述内联任务。在目标BeforeBuild中形成csproj文件:

  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.  -->
  <Import Project="SetBuildDate.targets" />
  <Target Name="BeforeBuild">
    <SetBuildDate FilePath="$(MSBuildProjectDirectory)\BuildMetadata.cs" />
  </Target>

FilePath属性被设置为Xamarin中的BuildMetadata.cs文件。表单项目,包含一个简单的类,具有字符串属性BuildDate,构建时间将被替换为:

public class BuildMetadata
{
    public static string BuildDate => "This can be any arbitrary string";
}

将此文件BuildMetadata.cs添加到项目中。它将在每次构建时被修改,但是以一种允许重复构建的方式(重复替换),因此您可以根据需要在源代码控制中包含或省略它。

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

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

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

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

string strCompTime = Properties.Resources.BuildDate;

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

我需要一个通用的解决方案,可以在任何平台(iOS、Android和Windows)上与NETStandard项目一起工作。为了实现这一点,我决定通过PowerShell脚本自动生成CS文件。下面是PowerShell脚本:

param($outputFile="BuildDate.cs")

$buildDate = Get-Date -date (Get-Date).ToUniversalTime() -Format o
$class = 
"using System;
using System.Globalization;

namespace MyNamespace
{
    public static class BuildDate
    {
        public const string BuildDateString = `"$buildDate`";
        public static readonly DateTime BuildDateUtc = DateTime.Parse(BuildDateString, null, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
    }
}"

Set-Content -Path $outputFile -Value $class

将PowerScript文件保存为GenBuildDate。Ps1,并添加到你的项目。最后,添加以下行到您的预构建事件:

powershell -File $(ProjectDir)GenBuildDate.ps1 -outputFile $(ProjectDir)BuildDate.cs

确保项目中包含BuildDate.cs。在任何操作系统上都像冠军一样!

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