Отображение даты сборки
В настоящее время у меня есть приложение, отображающее номер сборки в окне заголовка. Это хорошо, за исключением того, что это ничего не значит для большинства пользователей, которые хотят знать, есть ли у них последняя сборка - они склонны называть ее "прошлой четвергой", а не сборкой 1.0.8.4321.
План состоит в том, чтобы вместо этого указать дату сборки - например, "Приложение построено 21/10/2009".
Я изо всех сил пытаюсь найти программный способ вытащить дату сборки в виде текстовой строки для использования, как это.
Для номера сборки я использовал:
Assembly.GetExecutingAssembly().GetName().Version.ToString()
после определения, как они возникли.
Я хотел бы что-то подобное для даты компиляции (и время, для бонусных баллов).
Здесь очень ценятся указатели (извините за каламбур, если это уместно) или более аккуратные решения
33 ответа
Джеффу Этвуду было что сказать по этому поводу в " Определении даты сборки".
Оказывается, наиболее надежным методом является получение метки времени компоновщика из PE-заголовка, встроенного в исполняемый файл, - некоторый код C# (Джо Спиви) для этого из комментариев к статье Джеффа:
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)
Добавьте ниже, чтобы предварительно построить командную строку события:
echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"
Добавьте этот файл в качестве ресурса, теперь у вас есть строка 'BuildDate' в ваших ресурсах.
Чтобы создать ресурсы, смотрите Как создавать и использовать ресурсы в.NET.
Путь
Как указано @c00000fd в комментариях. Microsoft меняет это. И хотя многие люди не используют последнюю версию своего компилятора, я подозреваю, что это изменение делает этот подход, безусловно, плохим. И хотя это забавное упражнение, я бы порекомендовал людям просто вставлять дату сборки в свой бинарный файл любым другим необходимым способом, если важно отслеживать дату сборки самого бинарного файла.
Это можно сделать с помощью некоторой тривиальной генерации кода, которая, вероятно, уже является первым шагом в вашем скрипте сборки. Это и тот факт, что инструменты ALM/Build/DevOps очень помогают в этом, должны быть предпочтительны для всех.
Я оставляю остальную часть этого ответа здесь только в исторических целях.
Новый путь
Я передумал об этом, и в настоящее время использую этот трюк, чтобы получить правильную дату сборки.
#region Gets the build date and time (by reading the COFF header)
// http://msdn.microsoft.com/en-us/library/ms680313
struct _IMAGE_FILE_HEADER
{
public ushort Machine;
public ushort NumberOfSections;
public uint TimeDateStamp;
public uint PointerToSymbolTable;
public uint NumberOfSymbols;
public ushort SizeOfOptionalHeader;
public ushort Characteristics;
};
static DateTime GetBuildDateTime(Assembly assembly)
{
var path = assembly.GetName().CodeBase;
if (File.Exists(path))
{
var buffer = new byte[Math.Max(Marshal.SizeOf(typeof(_IMAGE_FILE_HEADER)), 4)];
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
fileStream.Position = 0x3C;
fileStream.Read(buffer, 0, 4);
fileStream.Position = BitConverter.ToUInt32(buffer, 0); // COFF header offset
fileStream.Read(buffer, 0, 4); // "PE\0\0"
fileStream.Read(buffer, 0, buffer.Length);
}
var pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
var coffHeader = (_IMAGE_FILE_HEADER)Marshal.PtrToStructure(pinnedBuffer.AddrOfPinnedObject(), typeof(_IMAGE_FILE_HEADER));
return TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1) + new TimeSpan(coffHeader.TimeDateStamp * TimeSpan.TicksPerSecond));
}
finally
{
pinnedBuffer.Free();
}
}
return new DateTime();
}
#endregion
По старому
Ну, как вы генерируете номера сборки? Visual Studio (или компилятор C#) фактически предоставляет номера автоматической сборки и ревизии, если вы измените атрибут AssemblyVersion на, например: 1.0.*
Что произойдет, так это то, что сборка будет равна числу дней с 1 января 2000 года по местному времени, а пересмотр будет равен числу секунд с полуночи по местному времени, разделенному на 2.
см. контент сообщества, номера автоматической сборки и ревизии
например AssemblyInfo.cs
[assembly: AssemblyVersion("1.0.*")] // important: use wildcard for build and revision numbers!
SampleCode.cs
var version = Assembly.GetEntryAssembly().GetName().Version;
var buildDateTime = new DateTime(2000, 1, 1).Add(new TimeSpan(
TimeSpan.TicksPerDay * version.Build + // days since 1 January 2000
TimeSpan.TicksPerSecond * 2 * version.Revision)); // seconds since midnight, (multiply by 2 to get original)
Один из подходов, о котором я удивляюсь, еще никто не упомянул, - это использование текстовых шаблонов T4 для генерации кода.
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ output extension=".g.cs" #>
namespace Foo.Bar
{
public static partial class Constants
{
public static DateTime CompilationTimestampUtc { get { return new DateTime(<# Write(DateTime.UtcNow.Ticks); #>L, DateTimeKind.Utc); } }
}
}
Плюсы:
- Locale-независимый
- Позволяет намного больше, чем просто время компиляции
Минусы:
- Применимо только к библиотекам, где вы контролируете источник
- Требуется настроить ваш проект (и сервер сборки, если он его не берет), чтобы выполнить шаблон на этапе предварительной сборки. (См. Также Т4 без VS).
Добавьте ниже, чтобы предварительно построить командную строку события:
echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"
Добавьте этот файл в качестве ресурса, теперь у вас есть строка 'BuildDate' в ваших ресурсах.
Вставив файл в Ресурс (как открытый текстовый файл), я получил к нему доступ через
string strCompTime = Properties.Resources.BuildDate;
Чтобы создать ресурсы, смотрите Как создавать и использовать ресурсы в.NET.
Здесь много хороших ответов, но я чувствую, что могу добавить свой из-за простоты, производительности (по сравнению с решениями, связанными с ресурсами), кроссплатформенности (также работает с Net Core) и отказа от любого стороннего инструмента. Просто добавьте эту цель msbuild в csproj.
<Target Name="Date" BeforeTargets="CoreCompile">
<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>
и теперь у вас есть Builtin.CompileTime
или же new DateTime(Builtin.CompileTime, DateTimeKind.Utc)
если тебе так нужно.
ReSharper это не понравится. Вы можете игнорировать его или добавить частичный класс в проект, но он все равно работает.
Для проектов.NET Core я адаптировал ответ Postlagerkarte, чтобы обновить поле Copyright для сборки, указав дату сборки.
Прямое редактирование csproj
Следующее может быть добавлено непосредственно к первому PropertyGroup
в csproj:
<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>
Альтернатива: свойства проекта Visual Studio
Или вставьте внутреннее выражение непосредственно в поле Copyright в разделе Package свойств проекта в Visual Studio:
Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))
Это может немного сбивать с толку, потому что Visual Studio оценит выражение и отобразит текущее значение в окне, но также соответствующим образом обновит файл проекта за кулисами.
Комплексное решение через Directory.Build.props
Вы можете шлепнуть <Copyright>
элемент выше в Directory.Build.props
файл в корне вашего решения, и он будет автоматически применен ко всем проектам в каталоге, при условии, что каждый проект не имеет своего значения авторского права.
<Project>
<PropertyGroup>
<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>
</PropertyGroup>
</Project>
Directory.Build.props: настроить сборку
Выход
Пример выражения даст вам авторское право, подобное этому:
Copyright © 2018 Travis Troyer (2018-05-30T14:46:23)
поиск
Вы можете просмотреть информацию об авторских правах в свойствах файла в Windows или получить ее во время выполнения:
var version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location);
Console.WriteLine(version.LegalCopyright);
Еще один способ. Нет файлов для добавления или встраивания, нет T4, нет сценариев предварительной сборки. У меня работает на Net Core.
Добавьте такой класс в свой проект:
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 = Assembly.GetExecutingAssembly();
var attr = Attribute.GetCustomAttribute(assembly, typeof(BuildDateTimeAttribute)) as BuildDateTimeAttribute;
if (DateTime.TryParse(attr?.Date, out DateTime dt)
return dt;
else
return null;
}
Что касается техники извлечения информации о дате / версии сборки из байтов PE-заголовка сборки, Microsoft изменила параметры сборки по умолчанию, начиная с Visual Studio 15.4. Новое значение по умолчанию включает в себя детерминированную компиляцию, которая делает действительную временную метку и автоматически увеличивающиеся номера версий ушедшими в прошлое. Поле метки времени все еще присутствует, но оно заполняется постоянным значением, которое является хешем того или иного, но не указанием времени сборки.
http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html Подробный фон здесь
Для тех, кто отдает предпочтение временной метке над детерминированной компиляцией, есть способ переопределить новое значение по умолчанию. Вы можете включить тег в файл.csproj интересующей сборки следующим образом:
<PropertyGroup>
...
<Deterministic>false</Deterministic>
</PropertyGroup>
Обновление: я поддерживаю решение для текстового шаблона T4, описанное в другом ответе здесь. Я использовал это, чтобы решить свою проблему чисто, не теряя преимущества детерминированной компиляции. Одно предостережение по этому поводу заключается в том, что Visual Studio запускает компилятор T4 только при сохранении файла.tt, а не во время сборки. Это может быть неудобно, если вы исключите результат.cs из управления исходным кодом (поскольку вы ожидаете, что он будет сгенерирован), а другой разработчик проверит код. Без сохранения у них не будет файла.cs. В nuget есть пакет (который называется AutoT4), который делает компиляцию T4 частью каждой сборки. Я еще не сталкивался с решением этой проблемы во время развертывания производства, но я ожидаю, что что-то подобное сделает это правильным.
Я просто новичок в C#, так что, возможно, мой ответ звучит глупо - я отображаю дату сборки с даты, когда исполняемый файл был последний раз записан:
string w_file = "MyProgram.exe";
string w_directory = Directory.GetCurrentDirectory();
DateTime c3 = File.GetLastWriteTime(System.IO.Path.Combine(w_directory, w_file));
RTB_info.AppendText("Program created at: " + c3.ToString());
Я пытался использовать метод File.GetCreationTime, но получил странные результаты: дата из команды была 2012-05-29, но дата из Window Explorer показала 2012-05-23. После поиска этого несоответствия я обнаружил, что файл, вероятно, был создан 2012-05-23 (как показано в проводнике Windows), но скопирован в текущую папку 2012-05-29 (как показано командой File.GetCreationTime) - так чтобы быть на безопасной стороне, я использую команду File.GetLastWriteTime.
ZAlek
Я просто делаю:
File.GetCreationTime(GetType().Assembly.Location)
В 2018 году некоторые из вышеперечисленных решений больше не работают или не работают с.NET Core.
Я использую следующий подход, который прост и работает для моего проекта.NET Core 2.0.
Добавьте следующее в ваш.csproj внутри PropertyGroup:
<Today>$([System.DateTime]::Now)</Today>
Это определяет PropertyFunction, к которой вы можете получить доступ в вашей команде pre build.
Ваша предварительная сборка выглядит так
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();
}
}
}
Вышеупомянутый метод может быть настроен для сборок, уже загруженных в процессе, используя изображение файла в памяти (в отличие от повторного чтения его из хранилища):
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;
}
}
Для тех, кому нужно время компиляции в 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).
Опция, не обсуждаемая здесь, заключается в вставке ваших собственных данных в AssemblyInfo.cs, поле "AssemblyInformationalVersion" кажется подходящим - у нас есть пара проектов, в которых мы выполняли нечто подобное в качестве шага сборки (однако я не совсем доволен способ, который работает, так что на самом деле не хочу воспроизводить то, что у нас есть).
Есть статья на эту тему по codeproject: http://www.codeproject.com/KB/dotnet/Customizing_csproj_files.aspx
Мне нужно было универсальное решение, которое работало бы с проектом NETStandard на любой платформе (iOS, Android и Windows). Для этого я решил автоматически сгенерировать файл CS с помощью скрипта PowerShell. Вот скрипт 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 и добавьте его в свой проект. Наконец, добавьте следующую строку в событие Pre-Build:
powershell -File $(ProjectDir)GenBuildDate.ps1 -outputFile $(ProjectDir)BuildDate.cs
Убедитесь, что BuildDate.cs включен в ваш проект. Работает как чемпион на любой ОС!
Другой, дружественный к PCL подход заключается в использовании встроенной задачи MSBuild для замены времени сборки в строку, возвращаемую свойством приложения. Мы успешно используем этот подход в приложении, которое имеет проекты Xamarin.Forms, Xamarin.Android и Xamarin.iOS.
РЕДАКТИРОВАТЬ:
Упростить переместив всю логику в SetBuildDate.targets
файл, и используя Regex
вместо простой замены строки, так что файл может быть изменен каждой сборкой без "сброса".
Определение встроенной задачи MSBuild (для этого примера сохраненное в файле SetBuildDate.targets, локальном для проекта Xamarin.Forms):
<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.Forms csproj в target BeforeBuild:
<!-- 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
свойство установлено в BuildMetadata.cs
файл в проекте Xamarin.Forms, который содержит простой класс со строковым свойством BuildDate
, в которое будет подставлено время сборки:
public class BuildMetadata
{
public static string BuildDate => "This can be any arbitrary string";
}
Добавить этот файл BuildMetadata.cs
проэктировать. Он будет изменяться при каждой сборке, но таким образом, чтобы допускались повторные сборки (повторные замены), так что вы можете включить или опустить его в управлении исходным кодом по желанию.
Вы можете использовать этот проект: https://github.com/dwcullop/BuildInfo
Он использует T4 для автоматизации отметки даты сборки. Существует несколько версий (разных веток), в том числе одна, которая дает вам Git Hash ветки, которую вы в данный момент извлекли, если вы любите подобные вещи.
Раскрытие: я написал модуль.
Полное пошаговое решение для Visual Studio 2019, подобное тому, которое я хотел бы найти, когда начинал много лет назад.
Добавить текстовый файл ресурсов
Получите доступ к свойствам вашего проекта: в обозревателе решений выберите свой проект, затем щелкните правой кнопкой мыши -> свойства или Alt+Enter. На вкладке "Ресурсы" выберите "Файлы" (Ctrl+5). Затем Добавить ресурс / Добавить новый текстовый файл. Во всплывающем сообщении введите название вашего ресурса, например
BuildDate
: это создаст новый текстовый файл
BuildDate.txt
в папке Project / Resources, включите его как файл проекта и зарегистрируйте как ресурс, к которому затем можно получить доступ через
Properties.Resources
в C# или
My.Resources
в VB.
Автоматически обновлять файл ресурсов при каждой сборке
Теперь вы можете указать Visual Studio записывать дату в этот файл каждый раз, когда он строит или перестраивает проект. Для этого перейдите на вкладку Compile в свойствах проекта, выберите Build Events и скопируйте / вставьте следующее в текстовое поле «Pre-Build event command line»:
powershell -Command "((Get-Date).ToUniversalTime()).ToString(\"s\") | Out-File '$(ProjectDir)Resources\BuildDate.txt'"
Эта строка найдет BuildDate.txt и напишет дату и время today / NowUtc в формате ISO8601, например
2021-09-07T16:08:35
Получите дату сборки во время выполнения, прочитав файл
Затем вы можете получить эту дату из своего кода во время выполнения с помощью следующего помощника (C#):
DateTime CurrentBuildDate = DateTime.Parse(Properties.Resources.BuildDate, null, System.Globalization.DateTimeStyles.RoundtripKind);
Кредиты
- Базовая идея: /questions/48081717/otobrazhenie-datyi-sborki/48081966#48081966
- Улучшено с помощью powershell: dbruning
- Стабильный парсинг ISO8601: как создать .NET DateTime из формата ISO 8601
Вы можете использовать событие проекта после сборки, чтобы записать текстовый файл в целевой каталог с текущей датой и временем. Затем вы можете прочитать значение во время выполнения. Это немного глупо, но это должно сработать.
Небольшая новость об ответе "Новый путь" от Джона.
Вам нужно построить путь вместо использования строки CodeBase при работе с ASP.NET/MVC
var codeBase = assembly.GetName().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
v_build.Text = System.IO.File.GetLastWriteTime(System.Reflection.Assembly.GetExecutingAssembly(). Местоположение).ToString("yy.MM.dd.HH.mm.ss");
Общий обзор шагов
Убедитесь, что в вашем проекте есть папка «Ресурсы».
Добавьте этот оператор в командную строку события перед сборкой:
echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"
Запустите сборку (создает файл BuildDate.txt), а затем добавьте этот файл в качестве ресурса.
Метод в вашем классе С# может получить доступ к содержимому файла:
string propertiesBuildDate = Properties.Resources.BuildDate;
На следующих изображениях экрана показаны подробные шаги.
Вы можете запустить дополнительный шаг в процессе сборки, который записывает отметку даты в файл, который затем может быть отображен.
На вкладке свойств проектов посмотрите вкладку событий сборки. Существует возможность выполнить команду до или после сборки.
Я использовал предложение Абдуррахима. Однако, это, казалось, дало странный формат времени и также добавило сокращение для дня как часть даты сборки; пример: вс 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);
Хорошая новость в том, что это сработало.
GetLastWriteTime
не изменится, если вы скопируете сборку в другое место.
public static class AssemblyExtensions
{
public static DateTime GetLinkerTime(this Assembly assembly)
{
return File.GetLastWriteTime(assembly.Location).ToLocalTime();
}
}
Используйте следующий код.
File.GetCreationTime(Assembly.GetExecutingAssembly().Location)
Он вернет дату создания последней dll. Если отладка запущена, она будет отображать текущую дату и время. Я изменил некоторый код из одного из ответов, потому что не мог прокомментировать ответ. Комментарий для дальнейшего обсуждения.
Я только что добавил команду события предварительной сборки:
powershell -Command Get-Date -Format 'yyyy-MM-ddTHH:mm:sszzz' > Resources\BuildDateTime.txt
в свойствах проекта, чтобы сгенерировать файл ресурсов, который затем будет легко читать из кода.
Для .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();