Yapım tarihini görüntüleme


260

Şu anda başlık penceresinde yapı numarasını gösteren bir uygulamam var. Bu iyi ve iyi, ancak en son sürüme sahip olup olmadıklarını bilmek isteyen kullanıcıların çoğu için hiçbir şey ifade etmiyor - 1.0.8.4321 oluşturmak yerine "geçen Perşembe" olarak adlandırmaya eğilimlidirler.

Plan orada inşa tarihi koymaktır - Yani "Örneğin App 21/10/2009 üzerine inşa".

Böyle bir kullanım için metin dizesi olarak derleme tarihi çıkarmak için programlı bir yol bulmak için mücadele ediyorum.

Yapı numarası için kullandım:

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

bunların nasıl ortaya çıktığını tanımladıktan sonra.

Derleme tarihi (ve bonus puanları için zaman) için böyle bir şey istiyorum.

Burada işaretçiler çok takdir (uygunsa mazeret puntosu) veya daha temiz çözümler ...


2
Basit senaryolarda çalışan montajların derleme verilerini elde etmek için sağlanan yolları denedim, ancak iki montaj birleştirildiyse, doğru oluşturma süresini alamıyorum, gelecekte bir saat .. herhangi bir öneri?

Yanıtlar:


356

Jeff Atwood'un Yapım Tarihini Belirlemede bu konuda zor söyleyecek birkaç şeyi vardı .

En güvenilir yöntem, linker zaman damgasını yürütülebilir dosyaya gömülü PE başlığından almaktır - yorumlardan Jeff'in makalesine bunun için bazı C # kodu (Joe Spivey tarafından):

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

Kullanım örneği:

var linkTimeLocal = Assembly.GetExecutingAssembly().GetLinkerTime();

GÜNCELLEME: Yöntem .Net Core 1.0 için çalışıyordu, ancak .Net Core 1.1 sürümünden sonra çalışmayı durdurdu (1900-2020 aralığında rasgele yıllar verir)


8
Bununla ilgili tonumu biraz değiştirdim, acutal PE başlığına girerken hala çok dikkatli olurdum. Ama söyleyebildiğim kadarıyla, bu PE şeyler sürüm numaralarını kullanmaktan çok daha güvenilir, ayrıca sürüm numaralarını derleme tarihinden ayrı olarak atamak istemiyorum.
John Leidegren

6
Bunu beğendim ve kullanıyorum, ancak son satırdaki ikinci satır .AddHours()oldukça hileli ve (sanırım) DST'yi dikkate almayacak. Yerel saatte istiyorsanız, temizleyiciyi dt.ToLocalTime();kullanmalısınız. Orta kısım da bir using()blokla büyük ölçüde basitleştirilebilir .
JLRishe

6
Evet, bu benim için .net çekirdeği ile de çalışmayı durdurdu (1940'lar, 1960'lar, vb.)
eoleary

7
PE başlığının kullanımı bugün iyi bir seçenek gibi görünse de, MS'in deterministik yapıları denediğini (bu başlığı işe yaramaz hale getireceğini) ve hatta belki de gelecekteki C # derleyici sürümlerinde (iyi nedenlerle) varsayılan hale getirdiğini belirtmek gerekir. İyi okuma: blog.paranoidcoding.com/2016/04/05/… ve burada .NET Core ile ilgili cevap (TLDR: "tasarım gereği"): developercommunity.visualstudio.com/content/problem/35873/…
Paweł Bulwan

13
Bunu artık bulamayanlar için sorun bir .NET Core sorunu değildir. Visual Studio 15.4 ile başlayan yeni derleme parametresi varsayılanları hakkında aşağıdaki cevabımı görün.
Tom

108

Etkinlik öncesi komut satırı oluşturmak için aşağıya ekleyin:

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

Bu dosyayı kaynak olarak ekleyin, artık kaynaklarınızda 'BuildDate' dizesi var.

Kaynak oluşturmak için bkz . .NET'te kaynak oluşturma ve kullanma .


4
Benden +1, basit ve etkili. Hatta böyle bir kod satırı ile dosyadan değer almayı başardı: String buildDate = <MyClassLibraryName>
.Properties.Resources.BuildDate

11
başka bir seçenek bir sınıf yapmaktır: (ilk kez derledikten sonra projeye dahil etmek zorundasınız) -> echo namespace My.app.namespace {public static class Build {public static string Timestamp = "% DATE%% TIME%" .Substring (0,16);}}> "$ (ProjectDir) \ BuildTimestamp.cs" - - - -> sonra Build.Timestamp
FabianSilva

9
Bu mükemmel bir çözüm. Tek sorun% date% ve% time% komut satırı değişkenlerinin yerelleştirilmesidir, bu nedenle çıktı kullanıcının Windows diline bağlı olarak değişir.
VS

2
+1, bu PE başlıklarını okumaktan daha iyi bir yöntemdir - çünkü bunun işe yaramayacağı birkaç senaryo vardır (örneğin Windows Phone Uygulaması)
Matt Whitfield

17
Zeki. Biçim üzerinde daha kesin denetim elde etmek için powershell'i de kullanabilirsiniz; örneğin, ISO8601 olarak biçimlendirilmiş bir UTC tarih saatini almak için: powershell -Command "((Get-Date) .ToUniversalTime ()). ToString (\" s \ ") | Dışarı Dosya '$ (ProjectDir) Resources \ BuildDate.txt' "
22'de dbruning

90

Yol

Yorumlarda @ c00000fd tarafından işaret edildiği gibi . Microsoft bunu değiştiriyor. Birçok kişi derleyicilerinin en son sürümünü kullanmasa da, bu değişikliğin bu yaklaşımı tartışmasız kötü hale getirdiğinden şüpheleniyorum. Ve eğlenceli bir alıştırma olsa da, ikilinin kendisinin derleme tarihini izlemek önemliyse, gerekli olan diğer yollarla ikili bir tarih oluşturma.

Bu, büyük olasılıkla derleme kodunuzda ilk adım olan bazı önemsiz kod üretimi ile yapılabilir. Bu ve ALM / Build / DevOps araçlarının bu konuda çok yardımcı olması ve başka herhangi bir şeye tercih edilmesi gerekir.

Bu cevabın geri kalanını burada sadece tarihi amaçlar için bırakıyorum.

Yeni yol

Bu konuda fikrimi değiştirdim ve şu anda doğru yapım tarihini almak için bu hileyi kullanıyorum.

#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

Eski yol

Yapı sayılarını nasıl üretiyorsunuz? AssemblyVersion özniteliğini örn. Olarak değiştirirseniz, Visual Studio (veya C # derleyicisi) aslında otomatik derleme ve düzeltme numaraları sağlar.1.0.*

Olan şey şu ki, yapı 1 Ocak 2000 yerel saatinden bu yana geçen gün sayısına eşit olacak ve revizyonun gece yarısı yerel saatinden bu yana saniye sayısına eşit olacağı, 2'ye bölünecek.

Topluluk İçeriği, Otomatik Derleme ve Düzeltme numaralarına bakın

Örneğin, 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
Ben sadece bir saat ekledi eğerTimeZone.CurrentTimeZone.IsDaylightSavingTime(buildDateTime) == true
e4rthdog

2
Ne yazık ki bu yaklaşımı kapsamlı bir şekilde incelemeden kullandım ve bu bizi üretimde ısırdı. Sorun, JIT derleyicisi PE üstbilgi başladığında değişti olmasıdır. Bu yüzden aşağı oy. Şimdi yükleme tarihini neden derleme tarihi olarak gördüğümüzü açıklamak için gereksiz 'araştırma' yapacağım.
Jason D

8
@JasonD Sorununuz hangi evrende bir şekilde sorunum haline geliyor? Sadece bu uygulamanın dikkate almadığı bir sorunla karşılaştığınız için bir aşağı oyu nasıl haklı çıkarırsınız? Bunu ücretsiz olarak aldınız ve kötü test ettiniz. Ayrıca başlığın JIT derleyicisi tarafından yeniden yazıldığına inandığınız şey nedir? Bu bilgileri işlem belleğinden veya dosyadan mı okuyorsunuz?
John Leidegren

6
Bir web uygulamasında çalıştırıyorsanız, .Codebase özelliğinin bir URL (dosya: // c: /path/to/binary.dll) gibi göründüğünü fark ettim. Bu, File.Exists çağrısının başarısız olmasına neden olur. CodeBase özelliği yerine "assembly.Location" kullanmak benim için sorunu çözdü.
mdryden

2
@JohnLeidegren: Bunun için Windows PE başlığına güvenmeyin. Windows 10 ve tekrarlanabilir sürümler oluşturulduğundan , IMAGE_FILE_HEADER::TimeDateStampalan rastgele bir sayıya ayarlanmıştır ve artık bir zaman damgası değildir.
c00000fd

51

Etkinlik öncesi komut satırı oluşturmak için aşağıya ekleyin:

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

Bu dosyayı kaynak olarak ekleyin, artık kaynaklarınızda 'BuildDate' dizesi var.

Dosyayı Kaynağa ekledikten sonra (genel metin dosyası olarak),

string strCompTime = Properties.Resources.BuildDate;

Kaynak oluşturmak için bkz . .NET'te kaynak oluşturma ve kullanma .


1
@DavidGorsline - bu diğer yanıtı belirttiği için yorum işareti doğru . Değişikliğinizi geri almak için yeterli itibarım yok, aksi takdirde bunu kendim yapardım.
Wai Ha Lee

1
@Wai Ha Lee - a) alıntıladığınız cevap aslında derleme tarihini / saatini almak için kod vermez. b) o cevaba (ki ben yapardım) yorum eklemek için yeterli itibara sahip değildim, sadece mesaj göndermek için. öylesine c) Ben insanlar bir alanda tüm ayrıntıları alabilir tam cevap vererek yayınlanmıştır ..
brewmanz

% Date% yerine Úte% görürseniz, buraya bakın: developercommunity.visualstudio.com/content/problem/237752/… Özetle şunu yapın: echo% 25date% 25% 25time% 25
Qodex

41

Hiç kimsenin bahsetmediği bir yaklaşım , kod üretimi için T4 Metin Şablonlarını kullanmaktır .

<#@ 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); } }
    }
}

Artıları:

  • Yerel bağımsız
  • Derleme zamanından çok daha fazlasını sağlar

Eksileri:


1
Şimdi bu en iyi cevap. En çok oy alan cevap haline gelmeden önce 324 puan :). Stackoverflow en hızlı tırmanıcıyı göstermek için bir yola ihtiyaç duyar.
pauldendulk

1
@pauldendulk, çok yardımcı olmaz, çünkü en çok oylanan cevap ve kabul edilen cevap neredeyse her zaman en hızlı oyları alır. Bu soruyu kabul ettiğim için bu soruya kabul edilen cevap + 60 / -2'ye sahip .
Peter Taylor

Kenelerinize bir .ToString () eklemeniz gerektiğine inanıyorum (aksi takdirde derleme hatası alıyorum). Bununla birlikte, burada dik bir öğrenme eğrisiyle karşılaşıyorum, bunu ana programda nasıl kullanacağınızı gösterebilir misiniz?
Andy

@Andy, ToString () konusunda haklısın. Kullanımı sadece Constants.CompilationTimestampUtc. VS sınıf ile bir C # dosyası oluşturmuyorsa, bunu nasıl yapacağınızı anlamanız gerekir, ancak cevap (en azından) VS sürümüne ve csproj dosyasının türüne bağlıdır, bu yüzden bu yazı için çok fazla ayrıntı.
Peter Taylor

1
Diğerlerinin merak etmesi durumunda, VS 2017'de çalışmasını sağlamak için gereken buydu: Bunu bir Tasarım Süresi T4 şablonu yapmak zorunda kaldım (Anlamak için bir süre aldı, önce bir Önişlemci şablonu ekledim). Ben de bu derleme dahil etmek zorunda kaldı: Microsoft.VisualStudio.TextTemplating.Interfaces.10.0 projeye referans olarak. Sonunda benim şablonum "Sistem kullanarak;" ad alanından önce, yoksa DateTime referansı başarısız oldu.
Andy

20

Derleme tarihi / sürüm bilgilerini bir derleme PE üstbilgisinin baytlarından çekme tekniği ile ilgili olarak, Microsoft Visual Studio 15.4 ile başlayan varsayılan derleme parametrelerini değiştirmiştir. Yeni varsayılan, geçerli bir zaman damgası ve otomatik olarak artan sürüm numaralarını geçmişte bırakan deterministik derlemeyi içerir. Zaman damgası alanı hala mevcut, ancak bir şeyin veya başka bir karma olan kalıcı bir değerle doldurulur, ancak oluşturma süresinin herhangi bir göstergesi değildir.

Burada bazı ayrıntılı arka plan

Deterministik derleme üzerinde yararlı bir zaman damgasına öncelik verenler için, yeni varsayılanı geçersiz kılmanın bir yolu vardır. İlgili derlemenin .csproj dosyasına aşağıdaki gibi bir etiket ekleyebilirsiniz:

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

Güncelleme: Burada başka bir cevapta açıklanan T4 metin şablonu çözümünü onaylıyorum. Deterministik derlemenin faydasını kaybetmeden sorunumu temiz bir şekilde çözdüm. Bununla ilgili bir uyarı, Visual Studio'nun T4 derleyicisini yalnızca .tt dosyası kaydedildiğinde çalıştırdığı, derleme sırasında çalıştırmasıdır. Bu, .cs sonucunu kaynak denetiminden hariç tutarsanız (oluşturulmasını beklediğinizden beri) ve başka bir geliştirici kodu kontrol ederse garip olabilir. Yeniden kaydetmeden .cs dosyası olmayacak. Nuget üzerinde (sanırım AutoT4 olarak adlandırılır) bir paket var, bu da T4 derlemesini her yapının bir parçası haline getiriyor. Üretim dağıtımı sırasında bununla ilgili çözümle henüz karşılaşmadım, ancak benzer bir şeyin doğru olmasını bekliyorum.


Bu benim eski cevap kullanan bir sln benim sorunum çözüldü.
pauldendulk

T4 ile ilgili dikkatiniz oldukça adil, ancak cevabımda zaten mevcut olduğunu unutmayın.
Peter Taylor

15

Ben sadece C # newbie öyleyse belki cevabım aptalca geliyor - Yürütülebilir dosyanın son yazıldığı tarihten itibaren derleme tarihini görüntülüyorum:

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 yöntemini kullanmaya çalıştım ama garip sonuçlar aldım: komuttan tarih 2012-05-29, ancak Pencere Gezgini'nden tarih 2012-05-23 gösterdi. Bu tutarsızlığı aradıktan sonra dosyanın büyük olasılıkla 2012-05-23'te oluşturulduğunu (Windows Gezgini tarafından gösterildiği gibi) buldum, ancak 2012-05-29'da geçerli klasöre kopyalandı (File.GetCreationTime komutu ile gösterildiği gibi) - bu yüzden güvenli tarafında olmak File.GetLastWriteTime komutunu kullanıyorum.

Zalek


4
Bunun sürücüler / bilgisayarlar / ağlar arasında yürütülebilir dosyayı kopyalamaya karşı kurşun geçirmez olup olmadığından emin değilim.
Gizli Rabbi

Bu akılda ilk şey geliyor ama güvenilir olmadığını biliyorum indirdikten sonra öznitelikleri güncellemez ağ üzerinden dosyaları taşımak için kullanılan birçok yazılım vardır, ben @ Abdurrahim cevabı ile gitmek istiyorum.
Mübarek

Bu eski olduğunu biliyorum, ama sadece bazı benzer kod ile INSTALL işlemi (en azından clickonce kullanırken) derleme dosya zaman güncelleştirir bulundu. Çok faydalı değil. Yine de bu çözüm için geçerli olacağından emin değilim.
bobwki

Muhtemelen gerçekten istiyorsun LastWriteTime, çünkü bu çalıştırılabilir dosyanın gerçekten güncellendiğini doğru bir şekilde yansıtır.
David R Tribble

Maalesef, yürütülebilir bir dosya yazma süresi derleme süresinin güvenilir bir göstergesi değildir. Etki alanınızın dışındaki her türlü şey nedeniyle dosya zaman damgası yeniden yazılabilir.
Tom

15

Burada çok büyük cevaplar var, ancak basitlik, performans (kaynakla ilgili çözümlere kıyasla) çapraz platform (Net Core ile de çalışıyor) ve herhangi bir 3. taraf aracının önlenmesi nedeniyle kendim ekleyebileceğimi hissediyorum. Sadece bu msbuild hedefini csproj'a ekleyin.

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

ve şimdi bu şekilde Builtin.CompileTimeya new DateTime(Builtin.CompileTime, DateTimeKind.Utc)da ihtiyacınız varsa.

ReSharper bundan hoşlanmayacak. Onu görmezden gelebilir veya projeye kısmi bir sınıf ekleyebilirsiniz, ancak yine de işe yarıyor.


Bunu ile inşa edebilir ve ASP.NET Core 2.1 yerel olarak (web sitelerini çalıştırmak) geliştirebilirsiniz ancak VS 2017'den web dağıtımı yayınlamak "Yerleşik 'adı geçerli bağlamda yok" hatası ile başarısız. EK: Builtin.CompileTimeBir ustura görünümünden erişiyorsam .
Jeremy Cook

Bu durumda sadece ihtiyacınız olduğunu düşünüyorum BeforeTargets="RazorCoreCompile"ama sadece bu aynı
projedeyken

cool, ancak oluşturulan nesneyi nasıl ifade ederiz? Bana öyle geliyor ki cevabın önemli bir kısmı eksik ...
Matteo

1
@Matteo, yanıtta belirtildiği gibi, "Builtin.CompileTime" veya "yeni DateTime (Builtin.CompileTime, DateTimeKind.Utc)" kullanabilirsiniz. Visual Studio IntelliSense bunu hemen görebilir. Eski bir ReSharper tasarım zamanında şikayet edebilir, ancak yeni sürümlerde bunu düzeltmiş gibi görünüyor. clip2net.com/s/46rgaaO
Dmitry Gusarov

Bu sürümü kullandım, böylece tarihi almak için ek kod gerekmiyor. Ayrıca resharper son sürümü ile şikayet etmez. <WriteLinesToFile File = "$ (IntermediateOutputPath) BuildInfo.cs" Lines = "Sistem% 3B dahili statik kısmi sınıfı BuildInfo {public static long DateBuiltTicks = $ ([System.DateTime] :: UtcNow.Ticks)% 3B public static DateTime DateBuilt => yeni DateTime (DateBuiltTicks, DateTimeKind.Utc)% 3B} "Üzerine Yaz =" true "/>
Softlion

13

.NET Core projeleri için, Derleme Telif Hakkı alanını derleme tarihi ile güncelleştirmek için Postlagerkarte'nin yanıtını uyarladım.

Doğrudan Düzenleme csproj

Aşağıdakiler doğrudan PropertyGroupcsproj'daki ilk karaktere eklenebilir:

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

Alternatif: Visual Studio Project Özellikleri

Veya iç ifadeyi doğrudan Visual Studio'daki proje özelliklerinin Paket bölümündeki Telif Hakkı alanına yapıştırın:

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

Bu biraz kafa karıştırıcı olabilir, çünkü Visual Studio ifadeyi değerlendirecek ve geçerli değeri pencerede görüntüleyecektir, ancak proje dosyasını sahne arkasında uygun şekilde güncelleyecektir.

Directory.Build.props aracılığıyla çözüm çapında

<Copyright>Yukarıdaki öğeyi Directory.Build.propsçözüm kökünüzdeki bir dosyaya yerleştirebilir ve her projenin kendi Telif Hakkı değerini sağlamadığı varsayılarak, dizindeki tüm projelere otomatik olarak uygulanmasını sağlayabilirsiniz.

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

Directory.Build.props: Yapınızı özelleştirin

Çıktı

Örnek ifade size bunun gibi bir telif hakkı verecektir:

Copyright © 2018 Travis Troyer (2018-05-30T14:46:23)

geri alma

Windows'taki dosya özelliklerinden telif hakkı bilgilerini görüntüleyebilir veya çalışma zamanında yakalayabilirsiniz:

var version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location);

Console.WriteLine(version.LegalCopyright);

11

Yukarıdaki yöntem , işlemin içinde zaten yüklü olan derlemeler için dosyanın görüntüsünü bellekteki (depolamadan yeniden okumak yerine) kullanarak değiştirilebilir:

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

Bu çerçeve 4.7 için bile harika çalışıyor Kullanım: Utils.GetLinkerDateTime (Assembly.GetExecutingAssembly (), null))
real_yggdrasil

Harika çalışıyor! Teşekkürler!
bobwki

10

Windows 8 / Windows Phone 8'de derleme süresini alması gereken herkes için:

    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'de derleme süresini alması gereken herkes için:

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

NOT: Her durumda bir sanal alanda çalışıyorsanız, yalnızca uygulamanızla dağıttığınız derleme zamanını elde edebilirsiniz. (yani, bu GAC'daki hiçbir şey üzerinde çalışmaz).


WP 8.1'de Meclis'i nasıl edinebileceğiniz aşağıda açıklanmıştır:var assembly = typeof (AnyTypeInYourAssembly).GetTypeInfo().Assembly;
André Fiedler

Kodunuzu her iki sistemde de çalıştırmak isterseniz ne olur? - bu yöntemlerden biri her iki platform için de geçerli mi?
bvdb

10

2018'de yukarıdaki çözümlerin bazıları artık çalışmıyor veya .NET Core ile çalışmıyor.

Basit ve .NET Core 2.0 projem için çalışan aşağıdaki yaklaşımı kullanıyorum.

PropertyGroup içindeki .csproj dosyanıza aşağıdakileri ekleyin:

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

Bu, ön derleme komutunuzda erişebileceğiniz bir PropertyFunction tanımlar .

Ön yapımınız şöyle görünür

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

BuildTimeStamp.txt dosyasının özelliğini Gömülü kaynak olarak ayarlayın.

Artık zaman damgasını şu şekilde okuyabilirsiniz

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

Sadece toplu komut komutlarını kullanarak derleme öncesi olaylardan BuildTimeStamp.txt oluşturmak da çalışır. Burada bir hata yaptığınızı unutmayın: Hedefinizi tırnak işaretleri içine almalısınız (örn. "$(ProjectDir)BuildTimeStamp.txt"), Aksi takdirde klasör adlarında boşluk olduğunda kesilir.
Nyerguds

Belki de kültür değişmez zaman biçimini kullanmak mantıklıdır. Bunun gibi: $([System.DateTime]::Now.tostring("MM/dd/yyyy HH:mm:ss"))yerine$([System.DateTime]::Now)
Ivan Kochurkin

9

Burada tartışılmayan seçenek kendi verilerinizi AssemblyInfo.cs dosyasına eklemek, "AssemblyInformationalVersion" alanı uygun görünüyor - bir oluşturma adımı gibi bir şey yaptığımız birkaç projemiz var (ancak böylece gerçekten elimizdekileri yeniden üretmek istemiyoruz).

Konu hakkında codeproject ile ilgili bir makale var: http://www.codeproject.com/KB/dotnet/Customizing_csproj_files.aspx


6

Herhangi bir platformda (iOS, Android ve Windows) bir NETStandard projesi ile çalışan evrensel bir çözüme ihtiyacım vardı. Bunu yapmak için otomatik olarak bir PowerShell betiği ile bir CS dosyası oluşturmaya karar verdim. İşte PowerShell betiği:

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 dosyasını GenBuildDate.ps1 kaydedin ve projenize ekleyin. Son olarak, Yapım Öncesi etkinliğinize aşağıdaki satırı ekleyin:

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

Projenize BuildDate.cs dosyasının dahil edildiğinden emin olun. Herhangi bir işletim sisteminde şampiyon gibi çalışır!


1
Bunu, svn komut satırı aracını kullanarak SVN düzeltme numarasını almak için de kullanabilirsiniz. Bununla buna benzer bir şey yaptım.
user169771

5

Sadece yapıyorum:

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

1
İlginç bir şekilde, hata ayıklamadan çalıştırılıyorsa, 'gerçek' tarih GetLastAccessTime ()
balint

4

Bu projeyi kullanabilirsiniz: https://github.com/dwcullop/BuildInfo

Derleme tarihi zaman damgasını otomatikleştirmek için T4'ü kullanır. Bu tür bir şey içindeyseniz, o anda kullanıma alınmış dalın Git Karma değerini veren birkaç sürüm (farklı dallar) vardır.

Açıklama: Modülü yazdım.


3

Farklı, PCL dostu bir yaklaşım, oluşturma süresini uygulamadaki bir özellik tarafından döndürülen bir dizeye koymak için MSBuild satır içi görevini kullanmak olacaktır. Bu yaklaşımı Xamarin.Forms, Xamarin.Android ve Xamarin.iOS projelerine sahip bir uygulamada başarıyla kullanıyoruz.

DÜZENLE:

Tüm mantığı SetBuildDate.targetsdosyaya taşıyarak ve Regex"sıfırlama" olmadan dosyanın her yapı tarafından değiştirilebilmesi için basit dize değiştirme yerine kullanarak basitleştirilmiştir.

MSBuild satır içi görev tanımı (bu örnek için Xamarin.Forms projesinde yerel olan bir SetBuildDate.targets dosyasına kaydedilir):

<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 hedefindeki Xamarin.Forms csproj dosyasında yukarıdaki satır içi görevi çağırmak:

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

FilePathTesiste ayarlandığında BuildMetadata.csbir dize özellik, basit bir sınıf içerir Xamarin.Forms projede dosyası BuildDateoluşturma süresi ikame edileceği içine,:

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

Bu dosyayı BuildMetadata.csprojeye ekleyin . Her derleme tarafından değiştirilecektir, ancak tekrarlanan derlemelere (yinelenen değiştirmeler) izin verecek şekilde değiştirilecektir, böylece istediğiniz gibi kaynak kontrolüne dahil edebilir veya atlayabilirsiniz.


2

Geçerli datetime ile hedef dizininize bir metin dosyası yazmak için bir proje post-build olayı kullanabilirsiniz. Daha sonra değeri çalışma zamanında okuyabilirsiniz. Biraz kibirli ama işe yaramalı.



2

Jhon'dan "Yeni Yol" cevabında küçük bir güncelleme.

ASP.NET/MVC ile çalışırken CodeBase dizesini kullanmak yerine yolu oluşturmanız gerekir

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

1

Oluşturma işleminde, daha sonra görüntülenebilecek bir dosyaya bir tarih damgası yazan ek bir adım başlatabilirsiniz.

Proje özellikleri sekmesinde derleme olayları sekmesine bakın. Bir pre veya post build komutu yürütme seçeneği vardır.


1

Abdurrahim'in önerisini kullandım. Bununla birlikte, garip bir zaman formatı vermiş gibi görünüyordu ve ayrıca yapım tarihinin bir parçası olarak günün kısaltmasını ekledi; örnek: Paz 12/24/2017 13: 21: 05.43. Sadece tarih gerekiyordu, bu yüzden alt dize kullanarak geri kalanını ortadan kaldırmak zorunda kaldım.

echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"Oluşturma öncesi etkinliğine ekledikten sonra aşağıdakileri yaptım:

string strBuildDate = YourNamespace.Properties.Resources.BuildDate;
string strTrimBuildDate = strBuildDate.Substring(4).Remove(10);

İyi haber şu ki işe yaradı.


Çok basit bir çözüm. Bunu sevdim. Ve biçim rahatsız ederse , komut satırından daha iyi bir biçim almanın yolları vardır .
Nyerguds

0

Bu bir Windows uygulamasıysa, yalnızca uygulamanın yürütülebilir yolunu kullanabilirsiniz: new System.IO.FileInfo (Application.ExecutablePath) .LastWriteTime.ToString ("yyyy.MM.dd")


2
Zaten ve bunu kullanarak cevap ve aynı zamanda tam kurşun geçirmez değil.
crashmstr

0

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


Bu diğer cevapla aynı şey yapmıyor mu?
Wai Ha Lee
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.