बिल्ड दिनांक प्रदर्शित करना


260

वर्तमान में मेरे पास शीर्षक विंडो में बिल्ड नंबर प्रदर्शित करने वाला एक ऐप है। यह अच्छी तरह से और अच्छा है इसके अलावा इसका मतलब है कि अधिकांश उपयोगकर्ताओं के लिए कुछ भी नहीं है, जो जानना चाहते हैं कि क्या उनके पास नवीनतम बिल्ड है - वे 1.0.8.4321 के बजाय इसे "पिछले गुरुवार" के रूप में संदर्भित करते हैं।

इसके बजाय बिल्ड डेट को वहां रखने की योजना है - उदाहरण के लिए "21/10-2009 को बनाया गया ऐप"।

मैं इस तरह से उपयोग के लिए एक पाठ स्ट्रिंग के रूप में निर्माण की तारीख बाहर खींचने के लिए एक प्रोग्रामेटिक तरीका खोजने के लिए संघर्ष कर रहा हूं।

बिल्ड नंबर के लिए, मैंने उपयोग किया:

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

परिभाषित करने के बाद कि वे कैसे आए।

मैं संकलन तिथि (और बोनस अंक के लिए) के लिए ऐसा कुछ चाहूंगा।

पॉइंटर्स यहाँ बहुत सराहना की (उपयुक्त हो तो बहाना सज़ा), या भोजनालय समाधान ...


2
मैंने असेंबली का बिल्ड डेटा प्राप्त करने के लिए आपूर्ति के तरीकों की कोशिश की, जो सरल परिदृश्यों में काम करता है, लेकिन अगर दो विधानसभाओं को एक साथ मिला दिया जाता है तो मुझे सही बिल्ड समय नहीं मिलता है, यह भविष्य में एक घंटे है .. कोई सुझाव?

जवाबों:


356

जेफ एटवुड ने बिल्ड डेट को कठिन तरीके से निर्धारित करने में इस मुद्दे के बारे में कुछ बातें कही थीं ।

निष्पादन योग्य फ़ाइल में एम्बेडेड पीई हेडर से लिंकर टाइमस्टैम्प को पुनः प्राप्त करने के लिए सबसे विश्वसनीय विधि निकलती है - जेफ के लेख के लिए टिप्पणियों से कुछ सी # कोड (जो स्पिवी द्वारा)।

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 कोर 1.0 के लिए विधि काम कर रही थी, लेकिन .Net कोर 1.1 रिलीज के बाद काम करना बंद कर दिया (1900-220 में बेतरतीब साल देता है)


8
मैंने इसके बारे में कुछ हद तक अपना लहजा बदल लिया है, मैं अभी भी बहुत सावधान हो जाऊंगा जब acutal PE हैडर में खुदाई होगी। लेकिन जहां तक ​​मैं बता सकता हूं, यह पीई सामान संस्करण संख्याओं का उपयोग करने की तुलना में बहुत अधिक विश्वसनीय है, इसके अलावा मैं निर्माण तिथि से अलग संस्करण संख्याओं को निर्दिष्ट नहीं करना चाहता हूं।
जॉन लीडग्रेन ने

6
मुझे यह पसंद है और मैं इसका उपयोग कर रहा हूं, लेकिन यह दूसरी अंतिम पंक्ति .AddHours()है, बल्कि हैकिश है और मुझे लगता है कि डीएसटी को ध्यान में नहीं रखा जाएगा। यदि आप इसे स्थानीय समय में चाहते हैं, तो आपको dt.ToLocalTime();इसके बजाय क्लीनर का उपयोग करना चाहिए । मध्य भाग को एक using()ब्लॉक के साथ बहुत सरल बनाया जा सकता है ।
JLRishe

6
हाँ, इसने मेरे साथ काम करना बंद कर दिया। नेट कोर के साथ (1940, 1960 के दशक, आदि)
eoleary

7
जबकि पीई हेडर का उपयोग आज एक अच्छा विकल्प लग सकता है, यह ध्यान देने योग्य है कि एमएस नियतात्मक बिल्ड (जो इस हेडर को बेकार में प्रस्तुत करेगा) के साथ प्रयोग कर रहा है और शायद भविष्य में सी # (अच्छे कारणों के लिए) के संकलक संस्करणों में भी डिफ़ॉल्ट बना रहा है। अच्छा पढ़ें: blog.paranoidcoding.com/2016/04/05/… और यहाँ .NET कोर (TLDR: "यह डिज़ाइन द्वारा है" से संबंधित उत्तर): developercommunity.visualstudio.com/content/problem/35873-…
Paweł Bulwan

13
उन लोगों के लिए जो इसे अब काम नहीं करते हैं, समस्या .NET कोर समस्या नहीं है। विजुअल स्टूडियो 15.4 के साथ शुरू होने वाले नए बिल्ड पैरामीटर डिफॉल्ट के बारे में नीचे मेरा उत्तर देखें।
टॉम

107

घटना-पूर्व कमांड लाइन बनाने के लिए नीचे जोड़ें:

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

इस फ़ाइल को संसाधन के रूप में जोड़ें, अब आपके पास अपने संसाधनों में 'BuildDate' स्ट्रिंग है।

संसाधन बनाने के लिए, .NET में संसाधन बनाने और उपयोग करने का तरीका देखें ।


4
सरल और प्रभावी मुझसे +1। मैंने इस तरह की कोड की एक पंक्ति के साथ फ़ाइल से मान प्राप्त करने में भी कामयाबी हासिल की: स्ट्रिंग buildDate = <MyClassLibraryName> .Properties.Resources.BuildDate
davidfrancis

11
एक अन्य विकल्प एक वर्ग बनाता है: (पहली बार के बाद परियोजना में शामिल करने के लिए इसे संकलित करें) -> इको नामस्थान My.app.namespace {सार्वजनिक स्थैतिक वर्ग बिल्ड {सार्वजनिक स्थिर स्ट्रिंग टाइमस्टैम्प = "% DATE%%%%" .Substring (0,16);}}> "$ (प्रोजेक्टडायर) \ BuildTimestamp.cs" - - - - - फिर इसे Build.Timestamp
FabianSilva

9
यह एक उत्कृष्ट उपाय है। एकमात्र समस्या यह है कि% दिनांक% और% समय% कमांड लाइन चर स्थानीयकृत हैं, इसलिए उपयोगकर्ता की विंडोज भाषा के आधार पर आउटपुट अलग-अलग होगा।
वीएस

2
+1, पीई हेडर पढ़ने की तुलना में यह एक बेहतर तरीका है - क्योंकि कई परिदृश्य हैं जहां यह बिल्कुल काम नहीं करेगा (उदाहरण के लिए विंडोज फोन ऐप)
मैट व्हिटफील्ड

17
चतुर। आप प्रारूप पर अधिक सटीक नियंत्रण प्राप्त करने के लिए भी पॉवरशेल का उपयोग कर सकते हैं, उदाहरण के लिए ISO8601 के रूप में स्वरूपित एक UTC डेटाइम प्राप्त करने के लिए: शक्तियां -Command "(Get-Date) .ToUniversalTime ())। ToString (\" s \ ")) | आउट-फाइल '$ (प्रोजेक्टडायर) संसाधन \ BuildDate.txt' "
dbruning

90

रास्ता

जैसा कि टिप्पणियों में @ c00000fd ने बताया है । Microsoft इसे बदल रहा है। और जबकि कई लोग अपने संकलक के नवीनतम संस्करण का उपयोग नहीं करते हैं मुझे संदेह है कि यह परिवर्तन इस दृष्टिकोण को निर्विवाद रूप से बुरा बनाता है। और जब यह एक मजेदार व्यायाम है, तो मैं लोगों को सलाह दूंगा कि वे किसी भी अन्य माध्यम से अपनी बायनरी में केवल एक बिल्ड डेट को एम्बेड करें, यदि बाइनरी के निर्माण की तारीख को ट्रैक करना महत्वपूर्ण है।

यह कुछ तुच्छ कोड पीढ़ी के साथ किया जा सकता है जो संभवतः आपकी बिल्ड स्क्रिप्ट में पहला कदम है। यह, और तथ्य यह है कि 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

पुराना तरीका है

खैर, आप बिल्ड नंबर कैसे उत्पन्न करते हैं? दृश्य स्टूडियो (या C # संकलक) वास्तव में स्वत: बिल्ड और संशोधन संख्या प्रदान करता है यदि आप असेंबली के लिए विधानसभा विशेषता को बदलते हैं1.0.*

क्या होगा कि यह है कि निर्माण 1 जनवरी, 2000 के स्थानीय दिनों के बाद की संख्या के बराबर होगा, और संशोधन के लिए स्थानीय समय के मध्य सेकंड के बाद की संख्या के बराबर, 2 से विभाजित होगा।

सामुदायिक सामग्री देखें, स्वचालित बिल्ड और संशोधन संख्या देखें

जैसे कि असेम्बलीइन्फो

[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
दुर्भाग्य से मैंने इस दृष्टिकोण का पूरी तरह से उपयोग किए बिना इसका उपयोग किया, और यह हमें उत्पादन में काट रहा है। समस्या यह है कि जब जेआईटी संकलक पीई हेडर जानकारी में बदल जाता है। इसलिए पतन। अब मुझे यह समझाने के लिए अनावश्यक अनुसंधान करना पड़ रहा है कि हम निर्माण तिथि को निर्माण तिथि के रूप में क्यों देखते हैं।
जेसन डी

8
@ जैसनड किस ब्रह्मांड में आपकी समस्या किसी तरह मेरी समस्या बन गई है? आप केवल एक डाउनवोट को सही क्यों ठहराते हैं क्योंकि आप एक ऐसे मुद्दे पर भागे हैं जिसे इस कार्यान्वयन ने ध्यान में नहीं लिया। आपको यह मुफ्त में मिला और आपने इसे खराब तरीके से परखा। इसके अलावा क्या आपको लगता है कि हेडर को जेआईटी कंपाइलर द्वारा फिर से लिखा जा रहा है? क्या आप यह जानकारी प्रोसेस मेमोरी या फाइल से पढ़ रहे हैं?
जॉन लीडेग्रेन

6
मैंने देखा है कि यदि आप एक वेब अनुप्रयोग में चल रहे हैं,। कोडबेस संपत्ति एक URL (फ़ाइल: // c: /path/to/binary.dll) प्रतीत होती है। यह फ़ाइल का कारण बनता है। एक्सपर्ट विफल होने के लिए कॉल करते हैं। कोडबेस संपत्ति के बजाय "असेंबली.लोकेशन" का उपयोग करना मेरे लिए समस्या को हल करता है।
mdryden 20

2
@ जॉनीलाइडग्रेन: उसके लिए विंडोज पीई हेडर पर निर्भर न रहें। चूंकि विंडोज़ 10 और प्रतिलिपि प्रस्तुत करने योग्य बनाता है , IMAGE_FILE_HEADER::TimeDateStampफ़ील्ड एक यादृच्छिक संख्या पर सेट है और अब समय-स्टैंप नहीं है।
c00000fd

51

घटना-पूर्व कमांड लाइन बनाने के लिए नीचे जोड़ें:

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

इस फ़ाइल को संसाधन के रूप में जोड़ें, अब आपके पास अपने संसाधनों में 'BuildDate' स्ट्रिंग है।

फ़ाइल को संसाधन (सार्वजनिक पाठ फ़ाइल के रूप में) में डालने के बाद, मैंने इसे एक्सेस किया

string strCompTime = Properties.Resources.BuildDate;

संसाधन बनाने के लिए, .NET में संसाधन बनाने और उपयोग करने का तरीका देखें ।


1
@DavidGorsline - टिप्पणी मार्कडाउन सही था क्योंकि यह इस अन्य उत्तर को उद्धृत कर रहा है । आपके परिवर्तन को रोलबैक करने के लिए मेरे पास अपर्याप्त प्रतिष्ठा है, अन्यथा मैंने इसे स्वयं किया होता।
वाई हा ली

1
@Wai हा ली - ए) आपके द्वारा उद्धृत उत्तर वास्तव में संकलन तिथि / समय को पुनः प्राप्त करने के लिए कोड नहीं देता है। b) उस समय मेरे पास उस उत्तर में टिप्पणी जोड़ने के लिए पर्याप्त प्रतिष्ठा नहीं थी (जो मैंने किया होगा), केवल पोस्ट करने के लिए। इसलिए c) मैंने पूरा जवाब देते हुए पोस्ट किया ताकि लोग एक क्षेत्र में सभी विवरण प्राप्त कर सकें ..
शराब बनानेवाला

यदि आप% दिनांक% के बजाय insteadte% देखते हैं, तो यहां देखें: developercommunity.visualstudio.com/content/problem/237752/… संक्षेप में, ऐसा करें: इको% 25date% 25% 25%% 25
Qodex

41

एक दृष्टिकोण जो मुझे आश्चर्यचकित करता है कि किसी ने अभी तक उल्लेख नहीं किया है वह कोड पीढ़ी के लिए टी 4 टेक्स्ट टेम्प्लेट का उपयोग करना है।

<#@ 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 अंक शीर्ष मतदान उत्तर बनने से पहले जाने के लिए :)। स्टैकओवरफ़्लो को सबसे तेज़ पर्वतारोही को दिखाने का एक तरीका चाहिए।
पॉलडेंडुलक

1
@pauldendulk, ज्यादा मदद नहीं करेगा, क्योंकि सबसे अधिक उत्तर दिया और स्वीकृत उत्तर लगभग हमेशा सबसे तेजी से वोट उठाते हैं। इस प्रश्न का स्वीकृत उत्तर + 60 / -2 है क्योंकि मैंने यह उत्तर पोस्ट किया है।
पीटर टेलर

मेरा मानना ​​है कि आपको अपने टिक्स में .ToString () जोड़ने की आवश्यकता है (मुझे अन्यथा एक संकलन त्रुटि मिलती है)। उस ने कहा, मैं यहाँ एक कठिन सीखने की अवस्था में चल रहा हूँ, क्या आप दिखा सकते हैं कि मुख्य कार्यक्रम में भी इसका उपयोग कैसे करें?
एंडी

@ और, आप ToString () के बारे में सही हैं। उपयोग सिर्फ है Constants.CompilationTimestampUtc। यदि VS वर्ग के साथ C # फ़ाइल उत्पन्न नहीं कर रहा है, तो आपको यह पता लगाने की आवश्यकता है कि इसे करने के लिए कैसे प्राप्त करें, लेकिन इसका उत्तर VS के संस्करण और बहुत कम (csproj) फ़ाइल के प्रकार पर निर्भर करता है, इसलिए यह है इस पोस्ट के लिए बहुत विस्तार से।
पीटर टेलर

1
यदि अन्य लोग आश्चर्यचकित हैं, तो यह वही है जो इसे वीएस 2017 पर काम करने के लिए लिया गया था: मुझे इसे एक डिज़ाइन टाइम टी 4 टेम्पलेट बनाना था (यह पता लगाने के लिए मुझे थोड़ी देर लगा, मैंने पहले एक प्रीप्रोसेसर टेम्पलेट जोड़ा)। मुझे इस असेंबली को भी शामिल करना था: Microsoft.VisualStudio.TextTemplating.Interfaces.10.0 परियोजना के संदर्भ के रूप में। अंत में मेरे टेम्पलेट में "सिस्टम का उपयोग करना" शामिल था; नाम स्थान से पहले, या अन्यथा DateTime का संदर्भ विफल रहा।
एंडी

20

असेंबली PE हेडर के बाइट्स से बिल्ड दिनांक / संस्करण जानकारी खींचने की तकनीक के बारे में, Microsoft ने Visual Studio 15.4 के साथ शुरू होने वाले डिफ़ॉल्ट बिल्ड पैरामीटर को बदल दिया है। नए डिफॉल्ट में नियतात्मक संकलन शामिल है, जो एक वैध टाइमस्टैम्प बनाता है और स्वचालित रूप से वर्धित संस्करण संख्याओं को अतीत की बात करता है। टाइमस्टैम्प क्षेत्र अभी भी मौजूद है, लेकिन यह एक स्थायी मूल्य से भर जाता है जो किसी चीज या अन्य का हैश है, लेकिन निर्माण समय का कोई संकेत नहीं है।

कुछ विस्तृत पृष्ठभूमि यहाँ

नियतात्मक संकलन पर एक उपयोगी टाइमस्टैम्प को प्राथमिकता देने वालों के लिए, नए डिफ़ॉल्ट को ओवरराइड करने का एक तरीका है। आप ब्याज की विधानसभा की .csproj फ़ाइल में एक टैग शामिल कर सकते हैं:

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

अपडेट: मैं यहां एक अन्य उत्तर में वर्णित टी 4 टेक्स्ट टेम्पलेट समाधान का समर्थन करता हूं। मैंने इसका उपयोग निर्धारक संकलन के लाभ को खोए बिना अपनी समस्या को हल करने के लिए किया। इसके बारे में एक सावधानी यह है कि Visual Studio केवल T4 कंपाइलर को चलाता है जब .tt फ़ाइल बच जाती है, बिल्ड टाइम पर नहीं। यह अजीब हो सकता है यदि आप स्रोत नियंत्रण से .cs परिणाम को बाहर करते हैं (क्योंकि आप इसे उत्पन्न होने की उम्मीद करते हैं) और एक अन्य डेवलपर कोड की जाँच करता है। फिर से शुरू किए बिना, उनके पास .cs फ़ाइल नहीं होगी। नगेट पर एक पैकेज है (मुझे लगता है कि ऑटोट 4 कहा जाता है) जो हर बिल्ड का टी 4 संकलन हिस्सा बनाता है। मैंने अभी तक उत्पादन परिनियोजन के दौरान इसके समाधान का सामना नहीं किया है, लेकिन मैं इसे ठीक करने के लिए कुछ इसी तरह की उम्मीद करता हूं।


इससे मेरी समस्या हल हो गई जिसमें सबसे पुराने उत्तर का उपयोग किया गया है।
पॉलडेंडुलक

टी 4 के बारे में आपकी सावधानी पूरी तरह से उचित है, लेकिन ध्यान दें कि यह पहले से ही मेरे जवाब में मौजूद है।
पीटर टेलर

15

मैं सिर्फ सी # नौसिखिया हूं इसलिए शायद मेरा जवाब मूर्खतापूर्ण है - मैं उस तारीख से बिल्ड तिथि प्रदर्शित करता हूं जिस दिन निष्पादन योग्य फ़ाइल को लिखा गया था:

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-05-23 को बनाई गई थी (जैसा कि विंडोज़ एक्सप्लोरर द्वारा दिखाया गया है), लेकिन 2012-05-29 को वर्तमान फ़ोल्डर में कॉपी की गई (जैसा कि फ़ाइल.गेटक्रिएशनटाइम कमांड द्वारा दिखाया गया है) - तो सुरक्षित पक्ष पर होने के लिए मैं File.GetLastWriteTime कमांड का उपयोग कर रहा हूं।

Zalek


4
मुझे यकीन नहीं है कि यह ड्राइव / कंप्यूटर / नेटवर्क पर निष्पादन योग्य कॉपी करने से बुलेट प्रूफ है।
चुपके रब्बी

यह पहली बात है जो ध्यान में आती है, लेकिन आपको पता है कि इसके विश्वसनीय नहीं हैं नेटवर्क पर फ़ाइलों को स्थानांतरित करने के लिए उपयोग किए जाने वाले कई सॉफ़्टवेयर हैं जो डाउनलोड करने के बाद विशेषताओं को अपडेट नहीं करते हैं, मैं @ अब्दुर्रहीम के उत्तर के साथ जाऊंगा।
मुबारशर

मुझे पता है कि यह पुराना है, लेकिन मुझे बस कुछ इसी तरह के कोड के साथ मिला है जो INSTALL प्रक्रिया (कम से कम क्लिकोन का उपयोग करते समय) असेंबली फ़ाइल समय को अपडेट करती है। बहुत उपयोगी नहीं है। सुनिश्चित नहीं है कि यह इस समाधान पर लागू होगा, हालांकि।
बॉबकी

आप शायद वास्तव में चाहते हैं LastWriteTime, क्योंकि यह सटीक रूप से उस समय को दर्शाता है जब निष्पादन योग्य फ़ाइल वास्तव में अपडेट की गई थी।
डेविड आर ट्रिबुलल

क्षमा करें, लेकिन एक निष्पादन योग्य फ़ाइल लिखने का समय बिल्ड समय का एक विश्वसनीय संकेत नहीं है। फाइल टाइम स्टैम्प को उन सभी प्रकार की चीजों के कारण फिर से लिखा जा सकता है जो आपके प्रभाव क्षेत्र के बाहर हैं।
टॉम

15

यहाँ बहुत सारे जवाब हैं, लेकिन मुझे लगता है कि मैं अपनी सादगी, प्रदर्शन (संसाधन-संबंधित समाधानों की तुलना) क्रॉस प्लेटफॉर्म (नेट कोर के साथ काम करता है) और किसी भी 3 पार्टी टूल से बचने के कारण अपना खुद का जोड़ सकता हूं। बस इस 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 से वेब परिनियोजन प्रकाशन "अंतर्निहित नाम" वर्तमान संदर्भ में त्रुटि के साथ विफल हो जाता है। ADDITION: अगर मैं Builtin.CompileTimeरेजर व्यू से एक्सेस कर रहा हूं ।
जेरेमी कुक

इस मामले में मुझे लगता है कि आपको सिर्फ जरूरत है BeforeTargets="RazorCoreCompile"लेकिन केवल इसी परियोजना में होने पर
दिमित्री गुसरोव

शांत, लेकिन हम उत्पन्न वस्तु को कैसे देखें? यह मुझे लगता है कि उत्तर में एक महत्वपूर्ण हिस्सा याद आ रहा है ...
Matteo

1
@ मैटेओ, जैसा कि उत्तर में बताया गया है, आप "Buildin.CompileTime" या "नई DateTime (बिलिन.कम्पाइलटाइम, DateTimeKind.Utc)" का उपयोग कर सकते हैं। विजुअल स्टूडियो इंटेलीसेनियन अभी इसे देखने में सक्षम है। एक पुराना रेस्परर डिज़ाइन समय में शिकायत कर सकता है, लेकिन ऐसा लगता है कि उन्होंने इसे नए संस्करणों में तय किया है। Clip2net.com/s/46rgaaO
दिमित्री

मैंने इस संस्करण का उपयोग किया है ताकि तारीख प्राप्त करने के लिए अतिरिक्त कोड की आवश्यकता न हो। इसके अलावा resharper अपने नवीनतम संस्करण के साथ शिकायत नहीं करता है। <WriteLinesToFile फ़ाइल = "$ (IntermediateOutputPath) BuildInfo.cs" लाइन्स = "सिस्टम% 3B आंतरिक स्थैतिक आंशिक वर्ग BuildInfo का उपयोग करके {सार्वजनिक स्थैतिक लंबी DateBuiltTicks = $ ([System.DateTime] :: UtcNow.Ticks)% 3B सार्वजनिक स्थैतिक DateTime दिनांक: => नया दिनांक समय (DateBuiltTicks, DateTimeKind.Utc)% 3B} "ओवरराइट =" सत्य "/>
सॉफ्टलियन

13

.NET कोर प्रोजेक्ट्स के लिए, मैंने बिल्ड दिनांक के साथ असेंबली कॉपीराइट फ़ील्ड को अपडेट करने के लिए Postlagerkarte के उत्तर को अनुकूलित किया।

सीधे csproj को संपादित करें

निम्नलिखित को सीधे PropertyGroupcsproj में पहले जोड़ा जा सकता है :

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

वैकल्पिक: विजुअल स्टूडियो प्रोजेक्ट गुण

या Visual Studio में प्रोजेक्ट गुण के पैकेज अनुभाग में सीधे कॉपीराइट फ़ील्ड में आंतरिक अभिव्यक्ति पेस्ट करें:

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

यह थोड़ा भ्रमित हो सकता है, क्योंकि विज़ुअल स्टूडियो अभिव्यक्ति का मूल्यांकन करेगा और विंडो में वर्तमान मूल्य प्रदर्शित करेगा, लेकिन यह प्रोजेक्ट फ़ाइल को पर्दे के पीछे उचित रूप से अपडेट करेगा।

डायरेक्ट्री के माध्यम से समाधान-चौड़ा ।uild.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 (असेंबली। GetExecutingAssembly (), नल))
real_yggdrasil

बहुत अच्छा काम करता है! धन्यवाद!
bobwki

10

विंडोज 8 / विंडोज फोन 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);
            }
        }
    }

विंडोज फोन 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;
एंड्रे फिडलर

यदि आप दोनों प्रणालियों पर अपना कोड चलाना चाहते हैं तो क्या होगा? - दोनों प्लेटफार्मों के लिए लागू इन तरीकों में से एक है?
bvdb

10

2018 में उपरोक्त कुछ समाधान अब काम नहीं करते हैं या .NET कोर के साथ काम नहीं करते हैं।

मैं निम्नलिखित दृष्टिकोण का उपयोग करता हूं जो सरल है और मेरी .NET कोर 2.0 परियोजना के लिए काम करता है।

प्रॉपर्टी ग्रुप के अंदर अपने .csproj में निम्नलिखित जोड़ें:

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

यह एक प्रॉपर्टीफिकेशन को परिभाषित करता है जिसे आप अपने प्री बिल्ड कमांड में एक्सेस कर सकते हैं।

आपका पूर्व-निर्माण इस तरह दिखता है

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

एंबेडेड संसाधन के लिए BuildTimeStamp.txt की संपत्ति सेट करें।

अब आप समय की मोहर इस तरह पढ़ सकते हैं

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)
इवान कोचूरिन 13

9

यहां चर्चा नहीं किए जाने वाले विकल्प में अपना खुद का डेटा असेंबलीइन्फो में डालना है। "असेंबली इनफॉर्मेशनवैल्यूएशन" फील्ड उपयुक्त लगता है - हमारे पास कुछ प्रोजेक्ट्स हैं, जहां हम एक बिल्ड स्टेप के समान कुछ कर रहे थे (हालांकि मैं पूरी तरह से खुश नहीं हूं जिस तरह से काम करता है वह वास्तव में जो हमें मिला है उसे पुन: उत्पन्न नहीं करना चाहता है)।

कोडप्रोजेक्ट पर इस विषय पर एक लेख है: http://www.codeproject.com/KB/dotnet/Customizing_csproj_files.aspx


6

मुझे एक सार्वभौमिक समाधान की आवश्यकता थी जो किसी भी प्लेटफॉर्म (आईओएस, एंड्रॉइड और विंडोज) पर एक नेटस्टैंडर्ड प्रोजेक्ट के साथ काम करता था। इसे पूरा करने के लिए, मैंने पावरशेल स्क्रिप्ट के माध्यम से स्वचालित रूप से एक सीएस फाइल बनाने का फैसला किया। यहाँ 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 के रूप में सहेजें और इसे अपनी परियोजना में जोड़ें। अंत में, अपने प्री-बिल्ड इवेंट में निम्न पंक्ति जोड़ें:

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

सुनिश्चित करें कि BuildDate.cs आपकी परियोजना में शामिल है। किसी भी ओएस पर एक विजेता की तरह काम करता है!


1
आप svn कमांड लाइन टूल का उपयोग करके SVN संशोधन संख्या प्राप्त करने के लिए भी इसका उपयोग कर सकते हैं। मैंने इसके साथ कुछ ऐसा ही किया है।
user169771

5

मैं बस करता हूँ:

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

1
दिलचस्प बात यह है डिबग से चल रहा है, 'सही' तारीख GetLastAccessTime () है
Balint

4

आप इस परियोजना का उपयोग कर सकते हैं: https://github.com/dwcullop/BuildInfo

यह T4 का निर्माण की तारीख टाइमस्टैम्प को स्वचालित करने के लिए करता है। कई वर्जन (अलग-अलग ब्रांच) हैं, जिनमें से एक है जो आपको वर्तमान में चेक आउट ब्रांच का Git हैश देता है, अगर आप उस तरह के हैं।

प्रकटीकरण: मैंने मॉड्यूल लिखा था।


3

एक अलग, पीसीएल-फ्रेंडली अप्रोच, बिल्ड टाइम को एक स्ट्रिंग में बदलने के लिए MSBuild इनलाइन कार्य का उपयोग करने के लिए होगा जो ऐप पर एक प्रॉपर्टी द्वारा लौटाया जाता है। हम Xamarin.Forms, Xamarin.Android और Xamarin.osOS परियोजनाओं वाले ऐप में इस दृष्टिकोण का सफलतापूर्वक उपयोग कर रहे हैं।

संपादित करें:

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>

Xamarin में उपरोक्त इनलाइन कार्य को आमंत्रित करना। लक्ष्य से पहले 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

झोन से "न्यू वे" उत्तर पर एक छोटा सा अपडेट।

आपको ASP.NET/MVC के साथ काम करते समय कोडबेस स्ट्रिंग का उपयोग करने के बजाय पथ बनाने की आवश्यकता है

    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

यदि यह एक विंडोज़ ऐप है, तो आप केवल एप्लिकेशन निष्पादन योग्य पथ का उपयोग कर सकते हैं: नया System.IO.FileInfo (Application.ExecutablePath) .LastWriteTime.ToString ("yyyy.MM.dd")


2
पहले से ही और इसका उपयोग करके जवाब दें, और बिल्कुल बुलेटप्रूफ भी नहीं।
क्रैश

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.