[C#] 빌드 날짜 표시

현재 제목 창에 빌드 번호를 표시하는 앱이 있습니다. 최신 빌드가 있는지 알고 싶어하는 대부분의 사용자에게는 아무런 의미가 없다는 점을 제외하고는 훌륭합니다. 빌드 1.0.8.4321이 아니라 “마지막 목요일”이라고 말하는 경향이 있습니다.

계획은 빌드 날짜를 대신하는 것입니다-예를 들어 “App built on 21/10/2009”.

빌드 날짜를 텍스트 문자열로 사용하여 프로그래밍 방식을 사용하는 프로그래밍 방식을 찾는 데 어려움을 겪고 있습니다.

빌드 번호에는 다음을 사용했습니다.

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

그 방법을 정의한 후.

컴파일 날짜 (및 보너스 포인트 시간)와 같은 것을 원합니다.

여기의 포인터는 (해당되는 경우 변명) 또는 더 좋은 해결책을 높이 평가했습니다 …



답변

Jeff Atwood는 빌드 날짜 결정 에서이 문제에 대해 할 말이별로 없었습니다 .

가장 신뢰할 수있는 방법은 실행 파일에 포함 된 PE 헤더 에서 링커 타임 스탬프를 검색하는 것으로 나타났습니다 .

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가 지적한대로 . 마이크로 소프트는 이것을 바꾸고있다. 많은 사람들이 최신 버전의 컴파일러를 사용하지 않지만이 변경으로 인해이 접근법이 의심 할 여지없이 나쁘다고 생각합니다. 그리고 재미있는 연습이지만 사람들이 바이너리 자체의 빌드 날짜를 추적하는 것이 중요하다면 필요한 다른 수단을 통해 빌드 날짜를 바이너리에 간단히 삽입하는 것이 좋습니다.

이것은 아마도 빌드 스크립트의 첫 번째 단계 일 수있는 사소한 코드 생성으로 수행 될 수 있습니다. 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

옛날 방식

글쎄, 어떻게 빌드 번호를 생성합니까? AssemblyVersion 특성을 다음과 같이 변경하면 Visual Studio (또는 C # 컴파일러)는 실제로 자동 빌드 및 수정 번호를 제공합니다.1.0.*

일어날 일은 2000 년 1 월 1 일 현지 시간 이후의 일 수와 같고 수정이 현지 시간 자정 이후의 초 수와 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)


답변

사전 빌드 이벤트 명령 행에 아래를 추가하십시오.

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

이 파일을 리소스로 추가하면 이제 리소스에 ‘BuildDate’문자열이 있습니다.

파일을 리소스에 공개 텍스트 파일로 삽입 한 후 다음을 통해 액세스했습니다.

string strCompTime = Properties.Resources.BuildDate;

리소스를 만들려면 .NET에서 리소스를 만들고 사용하는 방법을 참조하십시오 .


답변

아무도 언급하지 않은 한 가지 접근 방식 은 코드 생성에 T4 텍스트 템플릿 을 사용하는 것입니다.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ output extension=".g.cs" #>
using System;
namespace Foo.Bar
{
    public static partial class Constants
    {
        public static DateTime CompilationTimestampUtc { get { return new DateTime(<# Write(DateTime.UtcNow.Ticks.ToString()); #>L, DateTimeKind.Utc); } }
    }
}

장점 :

  • 로케일 독립적
  • 컴파일 시간보다 더 많은 것을 허용

단점 :


답변

어셈블리 PE 헤더의 바이트에서 빌드 날짜 / 버전 정보를 가져 오는 기술과 관련하여 Microsoft는 Visual Studio 15.4부터 기본 빌드 매개 변수를 변경했습니다. 새로운 기본값에는 결정적 컴파일이 포함되어있어 유효한 타임 스탬프와 자동으로 증가 된 버전 번호를 과거의 것으로 만듭니다. 타임 스탬프 필드는 여전히 존재하지만 무언가 또는 다른 것의 해시이지만 빌드 시간의 표시가 아닌 영구적 인 값으로 채워집니다.

여기에 몇 가지 자세한 배경

결정 론적 컴파일보다 유용한 타임 스탬프를 우선시하는 사람들에게는 새로운 기본값을 무시하는 방법이 있습니다. 관심있는 어셈블리의 .csproj 파일에 다음과 같이 태그를 포함 할 수 있습니다.

  <PropertyGroup>
      ...
      <Deterministic>false</Deterministic>
  </PropertyGroup>

업데이트 : 다른 답변에 설명 된 T4 텍스트 템플릿 솔루션을 추천합니다. 결정적 컴파일의 이점을 잃지 않고 문제를 깨끗하게 해결하는 데 사용했습니다. 주의 할 점은 Visual Studio는 빌드 시간이 아닌 .tt 파일을 저장할 때 T4 컴파일러 만 실행한다는 것입니다. 소스 컨트롤에서 .cs 결과를 제외하고 (생성 할 것으로 예상되므로) 다른 개발자가 코드를 체크 아웃하면이 작업이 어색 할 수 있습니다. 다시 저장하지 않으면 .cs 파일이 없습니다. 너겟에는 패키지가 있습니다 (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이지만 창 탐색기의 날짜는 2012-05-23을 보여줍니다. 이 불일치를 검색 한 후 파일이 2012 년 5 월 23 일 (Windows 탐색기로 표시)에 생성되었지만 2012 년 5 월 29 일 (현재 File.GetCreationTime 명령으로 표시)의 현재 폴더로 복사 된 것으로 확인되었습니다. File.GetLastWriteTime 명령을 사용하고 안전 측면에 있습니다.

잘렉