使用VS2019新建的项目生成后无法获取程序集正确的编译时间

使用VS2019新建的项目生成后无法获取程序集正确的编译时间

版本记录

时间说明
2020-06-16初稿

零、前言

一直以来,我都习惯将主程序的编译时间作为整个软件的发布时间,这样做的好处是,省去了每发布一个新版本就去关于界面中修改发布时间的操作。

关于获取程序集的编译时间,原理是读取可执行文件的PE结构中的TimeDateStamp这个属性,上网一搜就能找到现成的代码。之前我也是没动脑子,抄来就用了,倒是也没出现什么问题。

一、无法读取C++程序集的PE结构

直到有一天在读取一个使用C++编写的dll的编译时间时抛了异常,我才花了些时间了解了一下Windows可执行文件的头部结构,也就是PE32文件结构

初步了解PE文件结构后,很容易就找到了报错的原因,原本offset只读取了一个字节,普通的.NET程序集似乎都没问题,恰好这个C++程序集的偏移地址有两个字节,因此被识别为非法的程序集,改成ReadUInt16就好了。

由于本人也是新手,对PE32文件结构也是一知半解,不展开讨论了。

下面附上我改进过的代码:

/// <summary>
/// 获取PE程序集中的编译时间
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static DateTime GetPE32Time(string fileName)
{
    long 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.ReadUInt16();
        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.ReadUInt32();
    }
    return DateTime.SpecifyKind(new DateTime(1970, 1, 1), 
                                DateTimeKind.Utc).AddSeconds(seconds).ToLocalTime();
}

二、VS2019新建的项目读取到的PE时间戳错误

上面的问题是解决了,但别忘了,本文的主题是使用VS2019新建的项目生成后无法获取程序集正确的编译时间,换句话说,那个问题纯粹是因为我偷懒才引入的。

今天聊的这个问题是在无意间发现的,困扰了我很久,也很诡异。简单的说,就是使用VS2019新建的项目,读取到的PE结构中的TimeDateStamp是一个很大的数字,换算成时间,基本都是2100年以后。以前能正确读取时间的程序都是用2015或者更早版本的VS建的工程。

2.1 使用VS2012新建工程,读取时间戳

为了查找原因,我在虚拟机中装了VS2012,新建了一个控制台程序,编译后用EmEditor打开,得到的时间戳为0x5E 0xE8 0x5C 0x49。如下:

VS2012默认编译后的可执行文件

换算成十进制为1592286281,这是个Unix时间戳,换算成日期就是今天。

因此,使用VS2012新建的项目能正确读取到PE时间戳。

###2.2 使用VS2019新建工程,读取时间戳

同样,用VS2019新建一个控制台程序,编译后用EmEditor打开,得到的时间戳为0x82 0x6B 0x62 0x08。如下:

VS2019默认编译后的可执行文件

换算成十进制为2188075528,这个值明显偏大,用它算出来的日期就不对了。

2.3 比较两个csproj

根据我多年的经验,问题十有八九出在csproj这个文件身上。用工具对比两个版本的VS生成的工程文件,左边是VS2012,右边是VS2019。

比较两个版本VS生成的csproj

果不其然,VS2019生成的工程文件里这么一段:

<Deterministic>true</Deterministic>

直觉告诉我,问题就出在这里。

依靠万能的度娘,很快找到了这篇文章Roslyn 的确定性构建。原来这是C#编译器的一个特性,加上这个标记之后,每次生成的Hash值都是一样的,去掉后才会把时间编译进去。

如此,解决办法就简单了,把这行删掉即可。


参考资料

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值