ビルド日付の表示


260

現在、タイトルウィンドウにビルド番号を表示するアプリがあります。最新のビルドを持っているかどうかを知りたいほとんどのユーザーにとって何も意味がないことを除いて、それは良いことです。ビルド1.0.8.4321ではなく、「先週の木曜日」としてそれを参照する傾向があります。

代わりにビルド日付をそこに置く計画です-たとえば、「2009/10/21にビルドされたアプリ」など。

このように使用するために、ビルド日付をテキスト文字列として引き出すプログラム的な方法を見つけるのに苦労しています。

ビルド番号には、次のものを使用しました。

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

それらがどのようにして生まれたかを定義した後。

コンパイル日(とボーナスポイントの時間)については、そのようなものをお願いします。

ここでのポインターは高く評価されます(適切な場合は失礼な言い訳)、またはより適切な解決策...


2
単純なシナリオで機能するアセンブリのビルドデータを取得するために提供された方法を試してみましたが、2つのアセンブリをマージすると、正しいビルド時間が得られない場合、1時間後になります。

回答:


356

ジェフ・アトウッドは、この問題についてビルド日付決定することについて難しいことをいくつか述べました。

最も信頼できる方法は、実行可能ファイルに埋め込まれたPEヘッダーからリンカーのタイムスタンプを取得することです。Jeffの記事へのコメントから、いくつかのC#コード(Joe Spiveyによる)を取得します。

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
私はこれについて多少口調を変えましたが、実際のP​​Eヘッダーを掘り下げるときは、まだ非常に注意しています。しかし、私の知る限り、このPEはバージョン番号を使用するよりもはるかに信頼性が高く、ビルド日とは別にバージョン番号を割り当てたくありません。
John Leidegren

6
私はこれを気に入って使用していますが、最後から2行目.AddHours()はかなりハッキリしているため、DSTは考慮されません。現地時間で必要な場合は、dt.ToLocalTime();代わりにクリーナーを使用してください。中央部分もusing()ブロックで大幅に簡略化できます。
JLRishe 2013年

6
うん、これは同様に、.NETコア(1940、1960、など)と私のために動作を停止
eoleary

7
今日、PEヘッダーの使用は良いオプションのように思えるかもしれませんが、MSは決定論的ビルド(このヘッダーを役に立たないものにする)を実験しており、おそらくC#の将来のコンパイラバージョンでデフォルトにすることにも注意する価値があります(正当な理由により)。グッド読む:blog.paranoidcoding.com/2016/04/05/... .NETのコアに関連して、ここでの回答(TLDR:「それはデザインによってだ」):developercommunity.visualstudio.com/content/problem/35873/...
パヴェルBulwan

13
これが機能しなくなった場合、問題は.NET Coreの問題ではありません。Visual Studio 15.4以降の新しいビルドパラメーターのデフォルトについては、以下の私の回答を参照してください。
トム

107

ビルド前のイベントコマンドラインに以下を追加します。

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

このファイルをリソースとして追加すると、リソースに「BuildDate」文字列が含まれます。

リソースを作成するには、「。NETでリソースを作成して使用する方法」を参照してください。


4
私からの+1、シンプルで効果的。次のようなコード行を使用して、ファイルから値を取得することもできました。StringbuildDate = <MyClassLibraryName> .Properties.Resources.BuildDate
davidfrancis

11
別のオプションは、クラスを作成することです(最初にコンパイルした後、プロジェクトに含める必要があります)-> echo名前空間My.app.namespace {パブリック静的クラスビルド{パブリック静的文字列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を使用して、形式をより正確に制御することもできます。たとえば、UTCの日時をISO8601としてフォーマットすることができます:powershell -Command "((Get-Date).ToUniversalTime())。ToString(\" s \ ")| Out-File '$(ProjectDir)Resources \ BuildDate.txt' "
dbruning

90

コメントで @ c00000fdが指摘したように。マイクロソフトはこれを変更しています。また、多くの人々はコンパイラの最新バージョンを使用していませんが、この変更によりこのアプローチが間違いなく悪いものになると思います。楽しい演習ですが、バイナリ自体のビルド日付を追跡することが重要な場合は、必要な他の方法でバイナリにビルド日付を埋め込むことをお勧めします。

これは、おそらく既にビルドスクリプトの最初のステップである、いくつかの簡単なコード生成で行うことができます。それと、ALM /ビルド/ 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.*

これは、ビルドが2000年1月1日の現地時間からの日数に等しくなり、リビジョンが現地時間の午前0時からの秒数を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
私はちょうど場合は1時間を追加TimeZone.CurrentTimeZone.IsDaylightSavingTime(buildDateTime) == true
e4rthdog

2
残念ながら、私はこのアプローチを徹底的に検証することなく使用しました。問題は、JITコンパイラーがPEヘッダー情報をキックすると、変更されることです。したがって、反対票。ここで、インストール日付をビルド日付として表示する理由を説明するために、不要な「調査」を行うことになりました。
Jason D

8
@JasonDあなたの問題はどういう宇宙で私の問題になりますか?この実装で考慮されていない問題が発生したという理由だけで、反対票を正当化する方法を教えてください。あなたはこれを無料で入手し、それを十分にテストしていません。また、ヘッダーがJITコンパイラによって書き換えられていると思われる理由は何ですか。この情報をプロセスメモリまたはファイルから読み取っていますか?
John Leidegren、2014年

6
Webアプリケーションで実行している場合、.CodebaseプロパティがURL(file:// c:/path/to/binary.dll)のように見えることに気付きました。これにより、File.Exists呼び出しが失敗します。CodeBaseプロパティの代わりに「assembly.Location」を使用すると、問題が解決しました。
mdryden 2017

2
@JohnLeidegren:Windows PEヘッダーに依存しないでください。Windows 10再現可能なビルドにより、IMAGE_FILE_HEADER::TimeDateStampフィールドは乱数に設定され、タイムスタンプではなくなりました。
c00000fd

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)私は人々が1つの領域ですべての詳細を得ることができるように完全な答えを与えることを投稿しました
。– brewmanz

%date%ではなくÚte%が表示される場合は、こちらを確認してください:developercommunity.visualstudio.com/content/problem/237752/… 簡単に言えば、次のようにします:echo%25date%25%25time%25
Qodex

41

誰もまだ言及していないことに驚いた1つのアプローチは、コード生成に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です。
Peter Taylor

ティックに.ToString()を追加する必要があると思います(そうしないとコンパイルエラーが発生します)。とはいえ、ここでは急な学習曲線にぶつかっています。メインプログラムでこれを使用する方法も示していただけますか?
アンディ

@Andy、あなたはToString()について正しい。使い方はただConstants.CompilationTimestampUtc。VSがクラスでC#ファイルを生成していない場合は、それを行う方法を理解する必要がありますが、答えは(少なくとも)VSのバージョンとcsprojファイルのタイプに依存するため、この投稿には詳細が多すぎます。
Peter Taylor

1
他の人が不思議に思っている場合は、これがVS 2017で機能するために必要なことです。これをデザインタイムT4テンプレートにする必要がありました(少し時間をかけて、プリプロセッサテンプレートを最初に追加しました)。プロジェクトへの参照として、このアセンブリMicrosoft.VisualStudio.TextTemplating.Interfaces.10.0も含める必要がありました。最後に、テンプレートには「using System」を含める必要がありました。名前空間の前、または日付時刻への参照が失敗しました。
アンディ

20

アセンブリのPEヘッダーのバイトからビルドの日付/バージョン情報を取得する手法に関して、MicrosoftはVisual Studio 15.4以降、デフォルトのビルドパラメーターを変更しました。新しいデフォルトには確定的なコンパイルが含まれ、有効なタイムスタンプと自動的にインクリメントされたバージョン番号が過去のものになります。タイムスタンプフィールドは引き続き存在しますが、何かのハッシュである永続的な値で埋められますが、ビルド時間を示すものではありません。

ここにいくつかの詳細な背景

確定的なコンパイルよりも有用なタイムスタンプを優先する人のために、新しいデフォルトを上書きする方法があります。次のように、目的のアセンブリの.csprojファイルにタグを含めることができます。

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

更新:私はここの別の回答で説明されているT4テキストテンプレートソリューションを推奨しています。私はこれを使用して、確定的コンパイルの利点を失うことなく、問題をきれいに解決しました。Visual Studioは、ビルド時ではなく、.ttファイルが保存されたときにのみT4コンパイラーを実行することに注意してください。.csの結果をソースコントロールから除外すると(生成されることが予想されるため)、別の開発者がコードをチェックアウトすると、これは扱いにくい場合があります。再保存しないと、.csファイルはありません。T4コンパイルをすべてのビルドの一部にするnuget(AutoT4と呼ばれると思います)のパッケージがあります。私はまだ本番環境での展開中の解決策に直面していませんが、同様のことが正しく行われることを期待しています。


これは、最も古い回答を使用するSNLで私の問題を解決しました。
pauldendulk

T4に関するあなたの注意は完全に公平ですが、それは私の答えにすでに存在していることに注意してください。
Peter Taylor

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でした。この不一致を検索した後、ファイルはおそらくWindows-05-23で作成され(Windowsエクスプローラーで表示)、2012-05-29で現在のフォルダーにコピーされました(File.GetCreationTimeコマンドで表示)-so安全のため、File.GetLastWriteTimeコマンドを使用しています。

ザレク


4
これがドライブ/コンピューター/ネットワーク間で実行可能ファイルをコピーすることからの完全な証拠かどうかはわかりません。
Stealth Rabbi 2013年

これは最初に頭に浮かぶことですが、ネットワーク経由でファイルを移動するために使用されるソフトウェアの多くはダウンロード後に属性を更新しないため、信頼性が低いことがわかります。@ Abdurrahimの答えを使用します。
Mubashar 2014年

私はこれが古いことを知っていますが、INSTALLプロセスが(少なくともclickonceを使用するときに)アセンブリファイルの時間を更新する同様のコードを見つけました。あまり役に立たない。ただし、それがこのソリューションに適用されるかどうかはわかりません。
bobwki

LastWriteTime実行ファイルが実際に更新された時刻を正確に反映しているので、おそらく本当にが必要です。
デビッドRトリブル

実行可能ファイルの書き込み時間は、ビルド時間の信頼できる指標ではありません。ファイルタイムスタンプは、影響範囲外のあらゆる種類の事柄のために書き換えることができます。
トム

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.CompileTimenew DateTime(Builtin.CompileTime, DateTimeKind.Utc)、あなたがそれをそのように必要とするなら。

ReSharperはそれが好きではありません。彼を無視するか、プロジェクトに部分クラスを追加することもできますが、とにかく機能します。


これを使用してビルドし、ASP.NET Core 2.1でローカルで開発(Webサイトを実行)できますが、VS 2017からのWeb配置パブリッシングは、「現在のコンテキストに「ビルド名」が存在しません」というエラーで失敗します。補足:Builtin.CompileTimeRazorビューからアクセスしている場合。
ジェレミークック

この場合、私はあなただけが必要BeforeTargets="RazorCoreCompile"だと思いますが、これが同じプロジェクトにある間だけです
Dmitry Gusarov

クールですが、生成されたオブジェクトをどのように参照しますか?答えは重要な部分が欠けているように思えます...
マッテオ

1
@Matteo、回答で述べたように、「Builtin.CompileTime」または「new DateTime(Builtin.CompileTime、DateTimeKind.Utc)」を使用できます。Visual Studio IntelliSenseは、これをすぐに確認できます。古いReSharperは設計時に不満を言う可能性がありますが、新しいバージョンで修正されたようです。clip2net.com/s/46rgaaO
ドミトリーグサロフ

私はこのバージョンを使用したので、日付を取得するための追加のコードは必要ありません。また、resharperは最新バージョンに不満はありません。<WriteLinesToFile File = "$(IntermediateOutputPath)BuildInfo.cs" Lines = "using System%3B internal static partial class 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の回答を調整して、アセンブリのCopyrightフィールドをビルド日付で更新しました。

csprojを直接編集する

次のものはPropertyGroup、csprojの最初のものに直接追加できます。

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

この1でもフレームワーク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;
アンドレ・フィードラー

両方のシステムでコードを実行したい場合はどうなりますか?-これらの方法の1つは両方のプラットフォームに適用できますか?
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 resourceに設定します。

今、あなたはこのようにタイムスタンプを読むことができます

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)
イワン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 2018

5

私はただします:

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

1
興味深いことに、デバッグから実行している場合、「真」の日付はGetLastAccessTime()です
balint

4

このプロジェクトを使用できます:https : //github.com/dwcullop/BuildInfo

T4を利用してビルド日付のタイムスタンプを自動化します。そのようなことに夢中になっている場合、現在チェックアウトされているブランチのGitハッシュを提供するバージョンを含む、いくつかのバージョン(異なるブランチ)があります。

開示:モジュールを作成しました。


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.cs文字列プロパティを持つ単純なクラスが含まれXamarin.Formsプロジェクト内のファイルBuildDateのビルド時間が置換されるには、:

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

このファイルBuildMetadata.csをプロジェクトに追加します。これはビルドごとに変更されますが、ビルドの繰り返し(置換の繰り返し)を可能にする方法で変更されるため、必要に応じてソース管理に組み込んだり省略したりできます。


2

プロジェクトのビルド後のイベントを使用して、現在の日時を含むテキストファイルをターゲットディレクトリに書き込むことができます。その後、実行時に値を読み取ることができます。それは少しハックですが、うまくいくはずです。



2

ジョンからの「新しい方法」の回答に関する小さな更新。

ASP.NET/MVCを使用する場合は、CodeBase文字列を使用する代わりにパスを作成する必要があります

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

1

ビルドプロセスで追加のステップを起動して、日付スタンプをファイルに書き込み、表示することができます。

プロジェクトのプロパティタブで、ビルドイベントタブを確認します。ビルド前またはビルド後のコマンドを実行するオプションがあります。


1

私はアブドゥラヒムの提案を使用しました。しかし、奇妙な時間形式を与えているようで、ビルド日の一部としてその日の省略形も追加されました。例:日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 2015年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.