新聞中心
我們?cè)诰帉?NET程序時(shí),經(jīng)常會(huì)在該程序的“關(guān)于本軟件”對(duì)話框中給出這個(gè)程序的編譯時(shí)間,如下圖所示:

創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括皋蘭網(wǎng)站建設(shè)、皋蘭網(wǎng)站制作、皋蘭網(wǎng)頁(yè)制作以及皋蘭網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,皋蘭網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到皋蘭省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
上圖中的編譯時(shí)間是如果得到的呢?其實(shí)是在其 C# 源程序中有這么一句:
- [assembly: AssemblyVersion("1.3.*")]
上述語(yǔ)句使用了 System.Reflection.AssemblyVersionAttribute 類,該類用于指定正在特性化的程序集的版本。在 MSDN 文檔中有以下描述:
- 程序集版本號(hào)是程序集標(biāo)識(shí)的一部分,在綁定到程序集時(shí)以及在版本策略中扮演著關(guān)鍵的角色。
- 版本號(hào)包含以下四部分:<主版本(Major)>.<次版本(Minor)>.<內(nèi)部版本號(hào)(Build)>.<修訂號(hào)(Revision)>
- 您可以指定所有這些值,也可使用星號(hào) (*) 表示接受默認(rèn)的內(nèi)部版本號(hào)、修訂號(hào),或者接受二者。 例如,
- [assembly:AssemblyVersion("2.3.25.1")] 指示主版本為 2,次版本為 3,內(nèi)部版本號(hào)為 25,修訂號(hào)為 1。
- 版本號(hào) [assembly:AssemblyVersion("1.2.*")] 指定主版本為 1,次版本為 2,并接受默認(rèn)的內(nèi)部版本號(hào)和修訂號(hào)。
- 版本號(hào) [assembly:AssemblyVersion("1.2.15.*")] 指定主版本為 1,次版本為 2,內(nèi)部版本號(hào)為 15,并接受默認(rèn)的修訂號(hào)。
- 默認(rèn)的內(nèi)部版本號(hào)每日增加。 默認(rèn)修訂號(hào)是隨機(jī)的。
具體來(lái)說(shuō),默認(rèn)的內(nèi)部版本號(hào)表示自2000年1月1日以來(lái)的天數(shù),而默認(rèn)修訂號(hào)也不是隨機(jī)的,表示自該天午夜零時(shí)以來(lái)的秒數(shù)的一半。于是就可以使用下面的表達(dá)式獲得 .NET 程序的編譯時(shí)間:
- new DateTime(2000, 1, 1).AddDays(version.Build).AddSeconds(version.Revision * 2)
但是,還有很多 .NET 程序的程序集版本號(hào)沒(méi)有使用星號(hào)來(lái)接受默認(rèn)的內(nèi)部版本號(hào)、修訂號(hào),就不能使用這個(gè)方法了。我們知道,.NET 程序也是一個(gè)標(biāo)準(zhǔn)的32位或64位的 Microsoft Windows 可執(zhí)行體(PE32,Portable Executable)文件。而PE文件頭中包含一個(gè)時(shí)間標(biāo)記來(lái)指出文件的生成時(shí)間,請(qǐng)參見(jiàn):“Microsoft可移植可執(zhí)行文件和通用目標(biāo)文件格式文件規(guī)范v8.1修訂版”和“Microsoft Portable Executable and Common Object File Format Specification”。具體來(lái)說(shuō)就是:
PE文件頭由Microsoft MS-DOS?占位程序、PE文件簽名、COFF文件頭以及可選文件頭組成,COFF目標(biāo)文件頭由COFF文件頭和可選文件頭組成。在這兩種情況下,文件頭后面緊跟著的都是節(jié)頭。
MS-DOS占位程序是一個(gè)運(yùn)行于MS?DOS下的合法應(yīng)用程序,它被放在EXE映像的最前面。在位置0x3C處,這個(gè)占位程序包含PE文件簽名的偏移地址。
在MS-DOS占位程序后面、在偏移0x3C指定的文件偏移處,是一個(gè)4字節(jié)的簽名,它用來(lái)標(biāo)識(shí)文件為一個(gè)PE格式的映像文件。這個(gè)簽名是“PE\0\0”(字母“P”和“E”后跟著兩個(gè)空字節(jié))。
緊跟著映像文件簽名之后,是一個(gè)標(biāo)準(zhǔn)COFF文件頭。在這個(gè)COFF文件頭的偏移為4開(kāi)始的4個(gè)字節(jié)就是從UTC時(shí)間1970年1月1日00:00起的總秒數(shù)(一個(gè)C運(yùn)行時(shí)time_t類型的值)的低32位,它指出文件何時(shí)被創(chuàng)建。
一個(gè)PE文件的例子如下圖所示:
使用 DumpBin.exe 可得到如下信息:
- Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
- Copyright (C) Microsoft Corporation. All rights reserved.
- Dump of file PowerWord2Snb.exe
- PE signature found
- File Type: EXECUTABLE IMAGE
- FILE HEADER VALUES
- 14C machine (x86)
- 3 number of sections
- 4D0165D7 time date stamp Fri Dec 10 07:27:19 2010
- 0 file pointer to symbol table
- 0 number of symbols
- E0 size of optional header
- 102 characteristics
- Executable
- 32 bit word machine
此外,在操作系統(tǒng)的文件系統(tǒng)中,也記錄了每個(gè)文件(不要求是PE文件)的創(chuàng)建和修改時(shí)間,如下圖所示:
我們寫一個(gè) C# 程序來(lái)獲取這三個(gè)時(shí)間吧:
- using System;
- using System.IO;
- using System.Reflection;
- [assembly: AssemblyVersion("1.0.*")]
- namespace Skyiv.BuildTime
- {
- sealed class Program
- {
- delegate DateTime GetTime(string fileName);
- TextWriter writer;
- Program(TextWriter writer)
- {
- this.writer = writer;
- }
- static void Main(string[] args)
- {
- Console.WriteLine("OS Version: " + Environment.OSVersion);
- Console.WriteLine("CLR Version: " + Environment.Version);
- Console.WriteLine();
- var fileName = (args.Length > 0) ? args[0] : Assembly.GetExecutingAssembly().Location;
- new Program(Console.Out).Write(fileName);
- }
- void Write(string fileName)
- {
- writer.WriteLine(fileName);
- Write("文件系統(tǒng) ", GetFileCreationTime, fileName);
- Write("PE32 ", GetPe32Time, fileName);
- rite("裝配件版本", GetAssemblyVersionTime, fileName);
- }
- void Write(string msg, GetTime getTime, string fileName)
- {
- string time;
- try
- {
- time = getTime(fileName).ToString("yyyy-MM-dd HH:mm:ss");
- }
- catch (Exception ex)
- {
- time = ex.Message;
- }
- writer.WriteLine("{0}: {1}", msg, time);
- }
- DateTime GetFileCreationTime(string fileName)
- {
- return new FileInfo(fileName).CreationTime;
- }
- DateTime GetAssemblyVersionTime(string fileName)
- {
- var version = Assembly.LoadFrom(fileName).GetName().Version;
- return new DateTime(2000, 1, 1).AddDays(version.Build).AddSeconds(version.Revision * 2);
- }
- DateTime GetPe32Time(string fileName)
- {
- int seconds;
- using (var br = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read)))
- {
- var bs = br.ReadBytes(2);
- var msg = "非法的PE32文件";
- if (bs.Length != 2) throw new Exception(msg);
- if (bs[0] != 'M' || bs[1] != 'Z') throw new Exception(msg);
- br.BaseStream.Seek(0x3c, SeekOrigin.Begin);
- var offset = br.ReadByte();
- br.BaseStream.Seek(offset, SeekOrigin.Begin);
- bs = br.ReadBytes(4);
- if (bs.Length != 4) throw new Exception(msg);
- if (bs[0] != 'P' || bs[1] != 'E' || bs[2] != 0 || bs[3] != 0) throw new Exception(msg);
- bs = br.ReadBytes(4);
- if (bs.Length != 4) throw new Exception(msg);
- seconds = br.ReadInt32();
- }
- return DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc).
- AddSeconds(seconds).ToLocalTime();
- }
- }
- }
這個(gè)程序的運(yùn)行結(jié)果如下所示:
- E:\work> BuildTime PowerWord2Snb.exe
- OS Version: Microsoft Windows NT 6.0.6002 Service Pack 2
- CLR Version: 2.0.50727.4206
- PowerWord2Snb.exe
- 文件系統(tǒng) : 2010-12-10 07:32:14
- PE32 : 2010-12-10 07:27:19
- 裝配件版本: 2010-12-10 07:27:18
- E:\work> BuildTime
- OS Version: Microsoft Windows NT 6.0.6002 Service Pack 2
- CLR Version: 2.0.50727.4206
- E:\work\BuildTime.exe
- 文件系統(tǒng) : 2010-12-18 20:26:24
- PE32 : 2010-12-18 19:56:41
- 裝配件版本: 2010-12-18 19:56:40
我們來(lái)看看著名的 .NET Reflector 的信息吧:
- E:\work> BuildTime d:\bin\reflector\reflector.exe
- OS Version: Microsoft Windows NT 6.0.6002 Service Pack 2
- CLR Version: 2.0.50727.4206
- d:\bin\reflector\reflector.exe
- 文件系統(tǒng) : 2008-07-10 19:36:45
- PE32 : 2010-07-15 00:42:36
- 裝配件版本: 2000-01-01 00:00:00
- E:\work>
再來(lái)更多的例子:
- E:\work> BuildTime C:\windows\regedit.exe
- OS Version: Microsoft Windows NT 6.0.6002 Service Pack 2
- CLR Version: 2.0.50727.4206
- C:\windows\regedit.exe
- 文件系統(tǒng) : 2008-04-23 19:35:29
- PE32 : 2008-01-19 13:30:16
- 裝配件版本: 未能加載文件或程序集“file:///C:\windows\regedit.exe”
- 或它的某一個(gè)依賴項(xiàng)。該模塊應(yīng)包含一個(gè)程序集清單。
- E:\work> BuildTime BuildTime.cs
- OS Version: Microsoft Windows NT 6.0.6002 Service Pack 2
- CLR Version: 2.0.50727.4206
- BuildTime.cs
- 文件系統(tǒng) : 2010-12-18 20:50:03
- PE32 : 非法的PE32文件
- 裝配件版本: 未能加載文件或程序集“file:///E:\work\BuildTime.cs”
- 或它的某一個(gè)依賴項(xiàng)。該模塊應(yīng)包含一個(gè)程序集清單。
- E:\work>
我們?cè)倏纯丛?Linux 操作系統(tǒng)中的情況(openSUSE 11.3, mono 2.8.1):
- ben@ben1520:~/work> dmcs BuildTime.cs
- ben@ben1520:~/work> mono BuildTime.exe
- OS Version: Unix 2.6.34.7
- CLR Version: 4.0.30319.1
- /home/ben/work/BuildTime.exe
- 文件系統(tǒng) : 2010-12-18 21:29:14
- PE32 : 2010-12-18 21:29:14
- 裝配件版本: 2000-01-01 00:00:00
- ben@ben1520:~/work> mono BuildTime.exe /usr/lib/mono/4.0/dmcs.exe
- OS Version: Unix 2.6.34.7
- CLR Version: 4.0.30319.1
- /usr/lib/mono/4.0/dmcs.exe
- 文件系統(tǒng) : 2010-11-12 22:52:48
- PE32 : 2010-11-12 22:44:24
- 裝配件版本: 2000-01-02 00:00:00
- ben@ben1520:~/work> mono BuildTime.exe /usr/bin/gcc-4.5
- OS Version: Unix 2.6.34.7
- CLR Version: 4.0.30319.1
- /usr/bin/gcc-4.5
- 文件系統(tǒng) : 2010-07-02 02:29:53
- PE32 : 非法的PE32文件
- 裝配件版本: Could not load file or assembly '/usr/bin/gcc-4.5' or one of its dependencies.
- An attempt was made to load a program with an incorrect format.
- ben@ben1520:~/work>
由上可見(jiàn),在 Linux 操作系統(tǒng)中 mono 對(duì) System.Reflection.AssemblyVersionAttribute 類的支持很成問(wèn)題。在 Microsoft 的 MSDN 文檔中說(shuō)可以用星號(hào)表示接受默認(rèn)的內(nèi)部版本號(hào)、修訂號(hào),默認(rèn)的內(nèi)部版本號(hào)每日增加。 默認(rèn)修訂號(hào)是隨機(jī)的。而 mono 的 C# 編譯器直接將默認(rèn)的內(nèi)部版本號(hào)和默認(rèn)的修訂號(hào)都設(shè)置為零了。
綜上所述,要得到 .NET 程序的編譯時(shí)間,還是去讀該程序的PE文件頭中的文件創(chuàng)建時(shí)間最靠譜。
原文鏈接:http://www.cnblogs.com/skyivben/archive/2010/12/18/1910180.html
【編輯推薦】
- .NET Framework字符串相關(guān)操作細(xì)節(jié)介紹
- 詳解.NET字符串解析的具體過(guò)程
- 漫談.NET開(kāi)發(fā)中的字符串編碼
- .NET Lambda表達(dá)式的語(yǔ)義:字符串列表范例
- 解釋.NET之匿名對(duì)象(AnonymousObject)
新聞名稱:深究.NET中程序的編譯時(shí)間
網(wǎng)頁(yè)URL:http://www.dlmjj.cn/article/dpgigjj.html


咨詢
建站咨詢
