Mostrando la fecha de construcción

Resuelto Mark Mayo asked hace 14 años • 32 respuestas

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...

Mark Mayo avatar Oct 21 '09 20:10 Mark Mayo
Aceptado

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.

mdb avatar Oct 21 '2009 13:10 mdb

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 .

Abdurrahim avatar Mar 19 '2013 14:03 Abdurrahim

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)
John Leidegren avatar Oct 21 '2009 14:10 John Leidegren

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.CompileTimeen 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.

Dmitry Gusarov avatar Jun 18 '2018 08:06 Dmitry Gusarov

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 PropertyGroupen 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.propsarchivo 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);
Travis Troyer avatar May 30 '2018 15:05 Travis Troyer