问题描述
如何将构建数据添加到程序集中并在关于 ASP.NET 5 MVC 应用程序的“关于”框中显示给用户?
在 .NET 4.7 MVC 应用程序中使用
//http://www.geekproject.com/showtopic.aspx?ID=21
// When you specify that you want to have buildnumber and revision automatically generated then //the compiler (C#) will generate buildnumber as the number of days from the 1st of January //2000. The revision is generated as the number of seconds from midnight divided by two,but it //will NOT take daylight savings time into account.
[assembly: AssemblyVersion("1.0.*")]
和
public static DateTime BuildDate
{
get
{
string version = Assembly.GetExecutingAssembly().FullName.Split(',')[1].Trim();
DateTime start = new DateTime(2000,1,1);
int buildNumber = Convert.ToInt32(version.Split('.')[2]);
int revision = Convert.ToInt32(version.Split('.')[3]);
return start.Add(new TimeSpan(buildNumber,2 * revision,0));
}
}
在 .NET 5 AssemblyVersion 中抛出
Visual Studio 2019 IDE 用于公共 MVC 应用程序。
解决方法
我使用这个功能很多年了。通用解决方案是使用互操作函数检索和解析始终存在的 VersionInfo 块。你的字符串解析是正确的;如果 Split 返回的项目少于 4 个或者它们不是数字,我会添加错误处理。
最初,日期没有考虑夏令时,但在 Visual Studios 2008 和 2017 之间,微软添加了这一点。我不知道什么时候会发生这种情况。
直到微软没有破坏向后兼容性:
DateTime buildDate = new DateTime(2000,1,1).ToUniversalTime().
AddDays(build).AddSeconds(revision * 2).ToLocalTime();
对于 Visual Studio 2017(可能更早)及更高版本:
DateTime buildDate = new DateTime(2000,1).
AddDays(build).AddSeconds(revision * 2));
我试图删除我能删除的所有内容,只搜索“Assebmly 版本”。代码示例仍然相当大,对此感到抱歉。请注意,根据区域设置,带有字符串的块 ("StringFileInfo") 可能会出现多次。
public string GetAssemblyVersion()
{
string versionString = "";
IntPtr temp;
string fileSpec = System.Reflection.Assembly.GetExecutingAssembly().Location;
uint size = GetFileVersionInfoSizeW(fileSpec,out temp);
if (size > 0)
{
IntPtr ptr = Marshal.AllocHGlobal((int)size);
if (GetFileVersionInfoW(fileSpec,out temp,size,ptr) != 0)
{
versionString = ScanVersionInfo(ptr,"Assembly Version");
}
Marshal.FreeHGlobal(ptr);
}
return versionString;
}
private string ScanVersionInfo(IntPtr ptr,string key)
{
int size = Marshal.ReadInt16(ptr);
byte[] buf = new byte[size];
Marshal.Copy(ptr,buf,size);
int fixedFileInfoSize = BitConverter.ToInt16(buf,2);
int fixedFileInfoOffset = pad32(2 + 2 + 2 + ("VS_VERSION_INFO".Length + 1) * 2);
int offset = pad32(fixedFileInfoOffset + fixedFileInfoSize);
return FindStringValue(buf,offset,key);
}
private string FindStringValue(byte[] buf,int offset,string key)
{
while (offset < buf.Length)
{
int wLength = BitConverter.ToUInt16(buf,offset); // item length with padding
string blockName = ASCIIEncoding.Unicode.GetString(buf,offset + 6,"StringFileInfo".Length * 2);
if (blockName == "StringFileInfo")
{
int offs = pad32(offset + 2 + 2 + 2 + ("StringFileInfo".Length + 1) * 2);
string unicodeKey = ASCIIEncoding.Unicode.GetString(buf,offs + 2 + 2 + 2,"12345678".Length * 2);
int itemOffset = pad32(offs + 2 + 2 + 2 + (unicodeKey.Length + 1) * 2);
int endOffset = BitConverter.ToInt16(buf,offs) + offs;
while (itemOffset < endOffset)
{
string value;
if (extractString(buf,ref itemOffset,out value) == key)
{
return value;
}
}
}
offset = pad32(offset + wLength); // advance to the next item
}
return "";
}
private string extractString(byte[] buf,ref int offset,out string value)
{
int wLength = BitConverter.ToUInt16(buf,offset); // item length with padding
int wValueLength = BitConverter.ToInt16(buf,offset + 2); // value length in words
int start = offset + 2 + 2 + 2; // key starts here
int size = wLength - 2 - 2 - 2 - wValueLength * 2; // tentative key length in bytes
string key = ASCIIEncoding.Unicode.GetString(buf,start,size); // extract key
key = key.Substring(0,key.IndexOf('\0')); // clean key's tail
start = pad32(start + (key.Length + 1) * 2); // value starts here
size = (wValueLength - 1) * 2; // exact value length in bytes
value = ASCIIEncoding.Unicode.GetString(buf,size); // extract value
offset = pad32(offset + wLength); // advance to the next item
return key;
}
private int pad32(int size)
{
return ((size + 3) / 4) * 4;
}
[DllImport("version.dll",CharSet = CharSet.Unicode)]
private static extern uint GetFileVersionInfoSizeW(string lptstrFilename,out IntPtr lpdwHandle);
[DllImport("version.dll",CharSet = CharSet.Unicode)]
private static extern uint GetFileVersionInfoW(string lptstrFilename,out IntPtr lpdwHandle,uint dwLen,IntPtr lpData);