빌드 날짜 표시


260

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

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

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

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

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

그 방법을 정의한 후.

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

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


2
간단한 시나리오에서 작동하는 어셈블리의 빌드 데이터를 얻으려면 제공된 방법을 시도했지만 두 어셈블리가 병합되면 올바른 빌드 시간을 얻지 못합니다. 향후 1 시간입니다. 제안 사항이 있습니까?

답변:


356

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 범위에서 임의의 년을 제공) 후에 작동이 중지되었습니다.


8
나는 이것에 대해 내 음색을 약간 변경했지만, acutal PE 헤더를 파고들 때 여전히 조심해야합니다. 그러나 내가 알 수있는 한,이 PE 물건은 버전 번호를 사용하는 것보다 훨씬 안정적이며, 빌드 날짜와 별도의 버전 번호를 할당하고 싶지 않습니다.
John Leidegren

6
나는 이것을 좋아하고 그것을 사용하고 있지만, 두 번째 줄의 마지막 줄 .AddHours()은 다소 해킹이며 DST를 고려하지 않을 것입니다. 현지 시간에 원한다면 클리너를 dt.ToLocalTime();대신 사용해야합니다 . 중간 부분도 using()블록 으로 크게 단순화 할 수 있습니다 .
JLRishe

6
예, .net 코어 (1940, 1960 등)에서도 작동이 중단되었습니다.
eoleary

7
PE 헤더 사용은 오늘날 좋은 옵션으로 보일 수 있지만 MS는 결정적 빌드를 실험하고 (이 헤더를 쓸모 없게 만들 것임) 향후 C #의 컴파일러 버전에서 기본값으로 만들 수도 있습니다 (좋은 이유가 있음). 잘 읽어 보세요 : blog.paranoidcoding.com/2016/04/05/… 그리고 여기에 .NET Core (TLDR : "it 's by design") 관련 답변이 있습니다 : developercommunity.visualstudio.com/content/problem/35873/…
Paweł Bulwan

13
이 문제가 더 이상 해결되지 않으면 .NET Core 문제가 아닙니다. Visual Studio 15.4로 시작하는 새로운 빌드 매개 변수 기본값에 대한 아래 답변을 참조하십시오.
Tom

107

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

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

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

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


4
간단하고 효과적인 +1 나는 심지어 다음과 같은 코드 줄을 사용하여 파일에서 값을 얻었습니다. String buildDate = <MyClassLibraryName> .Properties.Resources.BuildDate
davidfrancis

11
또 다른 옵션은 클래스를 만드는 것입니다. (컴파일 한 후 처음으로 프로젝트에 포함시켜야 함)-> echo namespace My.app.namespace {public static class Build {public static string Timestamp = "% DATE % % TIME %" .Substring (0,16);}}> "$ (ProjectDir) \ BuildTimestamp.cs"----> Build.Timestamp를 사용하여 호출 할 수 있습니다
FabianSilva

9
이것은 훌륭한 솔루션입니다. 유일한 문제는 % date % 및 % time % 명령 줄 변수가 현지화되어 있으므로 사용자의 Windows 언어에 따라 출력이 달라집니다.
VS

2
+1,이 방법은 PE 헤더를 읽는 것보다 낫습니다. 전혀 작동하지 않는 몇 가지 시나리오가 있기 때문에 (예 : Windows Phone 앱)
Matt Whitfield

17
영리한. powershell을 사용하여 형식을보다 정확하게 제어 할 수 있습니다 (예 : ISO8601로 형식화 된 UTC 날짜 시간 가져 오기) : powershell -Command "(((Get-Date) .ToUniversalTime ()). ToString (\"s \ ") | Out-File '$ (ProjectDir) Resources \ BuildDate.txt' "
dbruning

90

방법

의견 에서 @ 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)

3
난 그냥 일시간 경우 추가TimeZone.CurrentTimeZone.IsDaylightSavingTime(buildDateTime) == true
e4rthdog

2
불행히도 나는이 접근법을 철저히 조사하지 않고 사용했으며 생산에서 우리를 물고 있습니다. 문제는 JIT 컴파일러가 PE 헤더 정보를 시작하면 정보가 변경된다는 것입니다. 따라서 공감. 이제 설치 날짜를 빌드 날짜로 보는 이유를 설명하기 위해 불필요한 '연구'를 수행해야합니다.
Jason D

8
@JasonD 어떤 우주에서 문제가 어떻게 든 내 문제가됩니까? 이 구현에서 고려하지 않은 문제가 발생하여 다운 투표를 정당화하는 방법은 무엇입니까? 당신은 이것을 무료로 얻었고 그것을 잘못 테스트했습니다. 또한 JIT 컴파일러가 헤더를 다시 작성한다고 생각하는 이유는 무엇입니까? 프로세스 메모리 또는 파일에서이 정보를 읽고 있습니까?
John Leidegren

6
웹 응용 프로그램에서 실행하는 경우 .Codebase 속성이 URL (file : // c : /path/to/binary.dll) 인 것으로 나타났습니다. 이로 인해 File.Exists 호출이 실패합니다. CodeBase 속성 대신 "assembly.Location"을 사용하면 문제가 해결되었습니다.
mdryden

2
@ JohnLeidegren : Windows PE 헤더를 사용하지 마십시오. Windows 10재현 가능한 빌드 이므로 IMAGE_FILE_HEADER::TimeDateStamp필드는 임의의 숫자로 설정되며 더 이상 시간 소인이 아닙니다.
c00000fd 2019

51

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

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

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

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

string strCompTime = Properties.Resources.BuildDate;

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


1
@DavidGorsline- 이 다른 답변을 인용하고 있으므로 주석 표시가 정확했습니다 . 변경 사항을 롤백 할 평판이 충분하지 않습니다. 그렇지 않으면 직접 변경 한 것입니다.
Wai Ha Lee

1
@Wai Ha Lee-a) 인용 한 답변은 실제로 컴파일 날짜 / 시간을 검색하는 코드를 제공하지 않습니다. b) 그 대답에 의견을 추가 할만 큼 충분한 평판을 얻지 못했을 때 (내가했을 것), 게시 만. c) 사람들이 한 영역에서 모든 세부 사항을 얻을 수 있도록 완전한 답변을 제공했습니다 ..
brewmanz

% date % 대신 Úte %가 표시되면 여기를 확인하십시오. developercommunity.visualstudio.com/content/problem/237752/… 간단히 말해 : echo % 25date % 25 % 25time % 25
Qodex

41

아무도 언급하지 않은 한 가지 접근 방식 은 코드 생성에 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); } }
    }
}

장점 :

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

단점 :


1
이것이 가장 좋은 답입니다. 324 포인트가 최고 투표 답변이되기 전에 가야합니다. :). Stackoverflow는 가장 빠른 클라이머를 보여줄 방법이 필요합니다.
pauldendulk

1
@pauldendulk, 가장 도움이되지 않은 답변은 가장 많이 답한 답변과 승인 된 답변이 거의 항상 가장 빨리 투표를하기 때문입니다. 이 답변에 게시 한 이후이 질문에 대한 답변은 + 60 / -2 입니다.
피터 테일러

틱에 .ToString ()을 추가해야한다고 생각합니다 (그렇지 않으면 컴파일 오류가 발생합니다). 즉, 여기 가파른 학습 곡선을 겪고 있습니다. 주 프로그램에서도 이것을 사용하는 방법을 보여줄 수 있습니까?
Andy

@ 앤디, 당신은 ToString ()에 대해 옳습니다. 사용법은 Constants.CompilationTimestampUtc입니다. VS가 클래스로 C # 파일을 생성하지 않으면 그것을 얻는 방법을 알아야하지만 대답은 (최소한) VS 버전과 csproj 파일 유형에 달려 있습니다. 이 게시물에 대한 세부 정보가 너무 많습니다.
Peter Taylor

1
다른 사람들이 궁금해하는 경우 이것이 VS 2017에서 작동하게하는 데 필요한 것입니다. 디자인 타임 T4 템플릿으로 만들어야했습니다 (제발, 전 처리기 템플릿을 먼저 추가했습니다). 또한이 어셈블리를 프로젝트에 대한 참조로 Microsoft.VisualStudio.TextTemplating.Interfaces.10.0도 포함시켜야했습니다. 마지막으로 내 템플릿에는 "using System;"이 포함되어야했습니다. 네임 스페이스 앞에 있거나 DateTime에 대한 참조가 실패했습니다.
Andy

20

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

여기에 몇 가지 자세한 배경

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

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

업데이트 : 다른 답변에 설명 된 T4 텍스트 템플릿 솔루션을 추천합니다. 결정적 컴파일의 이점을 잃지 않고 문제를 깨끗하게 해결하는 데 사용했습니다. 주의 할 점은 Visual Studio는 빌드 시간이 아닌 .tt 파일을 저장할 때 T4 컴파일러 만 실행한다는 것입니다. 소스 컨트롤에서 .cs 결과를 제외하고 (생성 할 것으로 예상되므로) 다른 개발자가 코드를 체크 아웃하면이 작업이 어색 할 수 있습니다. 다시 저장하지 않으면 .cs 파일이 없습니다. 너겟에는 패키지가 있습니다 (AutoT4라고 생각합니다) .T4 컴파일을 모든 빌드의 일부로 만듭니다. 프로덕션 배포 중에 아직 이것에 대한 솔루션에 직면하지 않았지만 비슷한 것을 올바르게 할 것으로 기대합니다.


이것은 가장 오래된 대답을 사용하는 sln에서 내 문제를 해결했습니다.
pauldendulk

T4에 대한 귀하의주의는 완벽하게 공평하지만 이미 내 대답에 있습니다.
피터 테일러

15

나는 단지 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 명령을 사용하고 안전 측면에 있습니다.

잘렉


4
이것이 드라이브 / 컴퓨터 / 네트워크에서 실행 파일을 복사하는 것에 대한 방탄인지 확실하지 않습니다.
Stealth Rabbi

이것은 첫 번째 생각이지만, 다운로드 후 속성을 업데이트하지 않는 네트워크를 통해 파일을 이동하는 데 사용되는 많은 소프트웨어가 있다는 것이 확실하지 않습니다. @ Abdurrahim의 대답을 따르십시오.
Mubashar

나는 이것이 오래되었다는 것을 알고 있지만 INSTALL 프로세스 (적어도 clickonce를 사용할 때)가 어셈블리 파일 시간을 업데이트한다는 유사한 코드를 발견했습니다. 별로 유용하지 않습니다. 그러나이 솔루션에 적용되는지 확실하지 않습니다.
bobwki

LastWriteTime실행 파일이 실제로 업데이트 된 시간을 정확하게 반영하기 때문에 아마도을 원할 것입니다 .
David R Tribble

죄송하지만 실행 파일 쓰기 시간은 빌드 시간을 확실하게 나타내는 것은 아닙니다. 파일 타임 스탬프는 영향을받는 영역 외부에있는 모든 종류의 항목으로 인해 다시 작성할 수 있습니다.
Tom

15

여기에 많은 훌륭한 답변이 있지만 단순성, 성능 (자원 관련 솔루션과 비교) 크로스 플랫폼 (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는 그것을 좋아하지 않을 것입니다. 그를 무시하거나 프로젝트에 부분 클래스를 추가 할 수는 있지만 어쨌든 작동합니다.


이것으로 빌드하고 ASP.NET Core 2.1에서 로컬로 (웹 사이트를 실행) 개발할 수 있지만 VS 2017의 웹 배포 게시는 "현재 컨텍스트에 'Builtin'이라는 이름이 없습니다"라는 오류와 함께 실패합니다. 추가 사항 : Builtin.CompileTimeRazor보기에서 액세스 하는 경우 .
Jeremy Cook

이 경우 당신이 단지 필요가 있다고 생각 BeforeTargets="RazorCoreCompile"이 같은 프로젝트에있는 동안에 만 만
드미트리 Gusarov

멋지지만 생성 된 객체를 어떻게 참조합니까? 그것은 대답이 핵심 부분을 잃어버린 것 같습니다 ...
마테오

1
@Matteo는 답변에서 언급했듯이 "Builtin.CompileTime"또는 "new DateTime (Builtin.CompileTime, DateTimeKind.Utc)"를 사용할 수 있습니다. Visual Studio IntelliSense는이를 즉시 확인할 수 있습니다. 구식 ReSharper는 디자인 타임에 불만을 제기 할 수 있지만 새 버전에서는이를 수정 한 것처럼 보입니다. clip2net.com/s/46rgaaO
Dmitry Gusarov

이 버전을 사용했기 때문에 날짜를 얻기 위해 추가 코드가 필요하지 않습니다. 또한 resharper는 최신 버전을 불평하지 않습니다. <WriteLinesToFile File = "$ (IntermediateOutputPath) BuildInfo.cs"Lines = "시스템 % 3B 내부 정적 부분 클래스 BuildInfo 사용 {public static long DateBuiltTicks = $ ([System.DateTime] :: UtcNow.Ticks) % 3B public static DateTime DateBuilt => new DateTime (DateBuiltTicks, DateTimeKind.Utc) % 3B} "Overwrite ="true "/>
Softlion

13

.NET Core 프로젝트의 경우 Postlagerkarte의 답변을 수정하여 어셈블리 저작권 필드를 빌드 날짜로 업데이트했습니다.

직접 csproj 편집

PropertyGroupcsproj 의 첫 번째 항목 에 다음을 직접 추가 할 수 있습니다 .

<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>

대안 : Visual Studio 프로젝트 속성

또는 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);

11

메모리에서 파일의 이미지를 사용하여 (저장소에서 다시 읽지 않고) 프로세스 내에 이미로드 된 어셈블리에 대해 위의 방법을 조정할 수 있습니다 .

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;
    }
}

이것은 프레임 워크 4.7에서도 잘 작동합니다. 사용법 : Utils.GetLinkerDateTime (Assembly.GetExecutingAssembly (), null))
real_yggdrasil

잘 작동합니다! 감사!
bobwki

10

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의 어떤 것도 작동하지 않습니다).


WP 8.1에서 어셈블리를 얻는 방법은 다음과 같습니다.var assembly = typeof (AnyTypeInYourAssembly).GetTypeInfo().Assembly;
André Fiedler

두 시스템에서 코드를 실행하려면 어떻게해야합니까? -이 방법 중 하나가 두 플랫폼 모두에 적용 가능합니까?
bvdb

10

2018에서 위의 솔루션 중 일부는 더 이상 작동하지 않거나 .NET Core와 작동하지 않습니다.

간단하고 내 .NET Core 2.0 프로젝트에서 작동하는 다음 방법을 사용합니다.

PropertyGroup 내의 .csproj에 다음을 추가하십시오.

    <Today>$([System.DateTime]::Now)</Today>

이것은 PropertyFunction을 정의합니다사전 빌드 명령에서 액세스 할 수 을 .

사전 빌드는 다음과 같습니다

echo $(today) > $(ProjectDir)BuildTimeStamp.txt

BuildTimeStamp.txt의 특성을 Embedded 자원으로 설정하십시오.

이제 타임 스탬프를 다음과 같이 읽을 수 있습니다

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();
            }
        }
    }

배치 스크립트 명령을 사용하여 사전 빌드 이벤트에서 BuildTimeStamp.txt를 생성하는 것만으로 도 작동합니다. 실수 한 점에 유의하십시오. 대상을 따옴표로 묶어야합니다 (예 :). "$(ProjectDir)BuildTimeStamp.txt"또는 폴더 이름에 공백이 있으면 끊어집니다.
Nyerguds

문화 불변 시간 형식을 사용하는 것이 좋습니다. 이와 같이 : $([System.DateTime]::Now.tostring("MM/dd/yyyy HH:mm:ss"))대신$([System.DateTime]::Now)
Ivan Kochurkin

9

여기서 논의되지 않은 옵션은 AssemblyInfo.cs에 사용자 고유의 데이터를 삽입하는 것입니다. "AssemblyInformationalVersion"필드가 적절 해 보입니다. 빌드 단계와 비슷한 작업을 수행하는 몇 가지 프로젝트가 있습니다. 작동하는 방식은 실제로 우리가 가진 것을 재현하고 싶지는 않습니다).

codeproject에 관한 주제에 대한 기사가 있습니다 : http://www.codeproject.com/KB/dotnet/Customizing_csproj_files.aspx


6

모든 플랫폼 (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로 저장하고 프로젝트에 추가하십시오. 마지막으로 Pre-Build 이벤트에 다음 줄을 추가하십시오.

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

BuildDate.cs가 프로젝트에 포함되어 있는지 확인하십시오. 모든 OS에서 챔피언처럼 작동합니다!


1
이것을 사용하여 svn 명령 행 도구를 사용하여 SVN 개정 번호를 얻을 수도 있습니다. 나는 이것과 비슷한 것을했습니다.
user169771

5

나는 단지 :

File.GetCreationTime(GetType().Assembly.Location)

1
흥미롭게도 디버그에서 실행하는 경우 'true'날짜는 GetLastAccessTime ()
balint

4

이 프로젝트를 사용할 수 있습니다 : https://github.com/dwcullop/BuildInfo

T4를 활용하여 빌드 날짜 타임 스탬프를 자동화합니다. 현재 체크 아웃 된 지점의 Git Hash를 제공하는 버전을 포함하여 여러 가지 버전 (다른 지점)이 있습니다.

공개 : 나는 모듈을 썼다.


3

PCL에 친숙한 다른 방법은 MSBuild 인라인 작업을 사용하여 빌드 시간을 앱의 속성에서 반환하는 문자열로 대체하는 것입니다. Xamarin.Forms, Xamarin.Android 및 Xamarin.iOS 프로젝트가있는 앱에서이 방법을 성공적으로 사용하고 있습니다.

편집하다:

모든 논리를 SetBuildDate.targets파일 로 이동하고 Regex간단한 문자열 바꾸기 대신 사용하여 "재설정"하지 않고 각 빌드에서 파일을 수정할 수 있도록 단순화되었습니다.

MSBuild 인라인 작업 정의 (이 예제의 경우 Xamarin.Forms 프로젝트의 로컬 SetBuildDate.targets 파일에 저장 됨) :

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

대상 BeforeBuild의 Xamarin.Forms 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속성은 BuildMetadata.csXamarin.Forms 프로젝트에서 문자열 속성을 가진 간단한 클래스를 포함 하는 파일로 설정되며 BuildDate빌드 시간이 대체됩니다.

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

이 파일 BuildMetadata.cs을 프로젝트에 추가하십시오 . 모든 빌드에서 수정되지만 반복 빌드 (반복 대체)를 허용하는 방식으로 소스 제어에 원하는대로 포함하거나 생략 할 수 있습니다.


2

프로젝트 빌드 후 이벤트를 사용하여 현재 날짜 시간과 함께 대상 디렉토리에 텍스트 파일을 쓸 수 있습니다. 그런 다음 런타임에 값을 읽을 수 있습니다. 약간 해 키지 만 작동해야합니다.



2

Jhon의 "New Way"답변에 대한 작은 업데이트.

ASP.NET/MVC로 작업 할 때 CodeBase 문자열을 사용하는 대신 경로를 작성해야합니다.

    var codeBase = assembly.GetName().CodeBase;
    UriBuilder uri = new UriBuilder(codeBase);
    string path = Uri.UnescapeDataString(uri.Path);

1

빌드 프로세스에서 날짜 스탬프를 파일에 기록한 다음 표시 할 수있는 추가 단계를 시작할 수 있습니다.

프로젝트 속성 탭에서 빌드 이벤트 탭을보십시오. 사전 또는 사후 빌드 명령을 실행하는 옵션이 있습니다.


1

나는 Abdurrahim의 제안을 사용했습니다. 그러나 그것은 이상한 시간 형식을 제공하는 것처럼 보였고 또한 빌드 날짜의 일부로 하루의 약어를 추가했습니다. 예 : Sun 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);

여기서 좋은 소식은 그것이 효과가 있다는 것입니다.


매우 간단한 해결책. 나는 그것을 좋아한다. 그리고 형식이 귀찮 다면 명령 줄에서 더 나은 형식을 얻는 방법 이 있습니다 .
Nyerguds

0

이것이 Windows 앱인 경우 응용 프로그램 실행 경로를 사용할 수 있습니다. new System.IO.FileInfo (Application.ExecutablePath) .LastWriteTime.ToString ( "yyyy.MM.dd")


2
이것을 사용하여 이미 대답하고 정확하게 방탄하지는 않습니다.
crashmstr

0

그것은 수 Assembly execAssembly = Assembly.GetExecutingAssembly(); var creationTime = new FileInfo(execAssembly.Location).CreationTime; // "2019-09-08T14:29:12.2286642-04:00"


이 다른 답변 과 동일하지 않습니까?
Wai Ha Lee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.