Mostrando la fecha de construcción
Actualmente tengo una aplicación que muestra el número de compilación en la ventana de título. Eso está muy bien, excepto que no significa nada para la mayoría de los usuarios, que quieren saber si tienen la última versión; tienden a referirse a ella como "la del jueves pasado" en lugar de la versión 1.0.8.4321.
El plan es colocar la fecha de compilación allí; por ejemplo, "Aplicación creada el 21/10/2009".
Estoy luchando por encontrar una forma programática de extraer la fecha de compilación como una cadena de texto para usarla de esta manera.
Para el número de compilación, utilicé:
Assembly.GetExecutingAssembly().GetName().Version.ToString()
después de definir cómo surgieron.
Me gustaría algo así para la fecha de compilación (y la hora, para obtener puntos de bonificación).
Se agradecen mucho los consejos aquí (disculpe el juego de palabras si corresponde), o soluciones más interesantes...
Jeff Atwood tuvo algunas cosas que decir sobre este tema en Determinar la fecha de construcción de la manera más difícil .
El método más confiable resulta ser recuperar la marca de tiempo del vinculador del encabezado PE incrustado en el archivo ejecutable; algo de código C# (por Joe Spivey) para eso de los comentarios al artículo de Jeff:
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;
}
Ejemplo de uso:
var linkTimeLocal = Assembly.GetExecutingAssembly().GetLinkerTime();
Nota: este método funciona para .NET Core 1.0, pero dejó de funcionar después de .NET Core 1.1 ; proporciona años aleatorios en el rango 1900-2020.
Agregue lo siguiente a la línea de comando del evento precompilado:
echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"
Agregue este archivo como recurso, ahora tiene la cadena 'BuildDate' en sus recursos.
Para crear recursos, consulte Cómo crear y utilizar recursos en .NET .
El camino
Como lo señaló @ c00000fd en los comentarios . Microsoft está cambiando esto. Y aunque muchas personas no utilizan la última versión de su compilador, sospecho que este cambio hace que este enfoque sea incuestionablemente malo. Y si bien es un ejercicio divertido, recomendaría a las personas que simplemente incorporen una fecha de compilación en su binario a través de cualquier otro medio necesario si es importante realizar un seguimiento de la fecha de compilación del binario en sí.
Esto se puede hacer con alguna generación de código trivial que probablemente ya sea el primer paso en su script de compilación. Eso, y el hecho de que las herramientas ALM/Build/DevOps ayudan mucho con esto y deberían preferirse a cualquier otra cosa.
Dejo el resto de esta respuesta aquí solo con fines históricos.
la nueva manera
Cambié de opinión sobre esto y actualmente uso este truco para obtener la fecha de compilación correcta.
#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
la vieja manera
Bueno, ¿cómo se generan números de compilación? Visual Studio (o el compilador de C#) en realidad proporciona números de compilación y revisión automáticos si cambia el atributo AssemblyVersion a, por ejemplo1.0.*
Lo que sucederá es que la compilación será igual a la cantidad de días desde el 1 de enero de 2000, hora local, y la revisión será igual a la cantidad de segundos desde la medianoche, hora local, dividida por 2.
consulte Contenido de la comunidad, números de revisión y compilación automática
por ejemplo, AssemblyInfo.cs
[assembly: AssemblyVersion("1.0.*")] // important: use wildcard for build and revision numbers!
Código de muestra.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)
Hay muchas respuestas excelentes aquí, pero siento que puedo agregar la mía propia debido a la simplicidad, el rendimiento (en comparación con las soluciones relacionadas con recursos), la multiplataforma (también funciona con Net Core) y la evitación de cualquier herramienta de terceros. Simplemente agregue este objetivo msbuild al archivo 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>
y ahora tienes Builtin.CompileTime
en este proyecto, por ejemplo:
var compileTime = new DateTime(Builtin.CompileTime, DateTimeKind.Utc);
A ReSharper no le va a gustar. Puedes ignorarlo o agregar una clase parcial al proyecto también, pero funciona de todos modos.
UPD: Hoy en día, ReSharper tiene una opción en la primera página de Opciones: "Acceso a MSBuild", "Obtener datos de MSBuild después de cada compilación". Esto ayuda con la visibilidad del código generado.
Para proyectos .NET Core, adapté la respuesta de Postlagerkarte para actualizar el campo Copyright del ensamblado con la fecha de compilación.
Editar directamente csproj
Lo siguiente se puede agregar directamente al primero PropertyGroup
en csproj:
<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>
Alternativa: Propiedades del proyecto de Visual Studio
O pegue la expresión interna directamente en el campo Copyright en la sección Paquete de las propiedades del proyecto en Visual Studio:
Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))
Esto puede resultar un poco confuso, porque Visual Studio evaluará la expresión y mostrará el valor actual en la ventana, pero también actualizará el archivo del proyecto adecuadamente entre bastidores.
Toda la solución a través de Directory.Build.props
Puede colocar el <Copyright>
elemento anterior en un Directory.Build.props
archivo en la raíz de su solución y aplicarlo automáticamente a todos los proyectos dentro del directorio, asumiendo que cada proyecto no proporciona su propio valor de Copyright.
<Project>
<PropertyGroup>
<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>
</PropertyGroup>
</Project>
Directory.Build.props: personaliza tu compilación
Producción
La expresión de ejemplo le otorgará derechos de autor como este:
Copyright © 2018 Travis Troyer (2018-05-30T14:46:23)
Recuperación
Puede ver la información de derechos de autor desde las propiedades del archivo en Windows o obtenerla en tiempo de ejecución:
var version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location);
Console.WriteLine(version.LegalCopyright);