我多年来一直使用此功能。通用解决方案是使用互操作函数检索和解析始终存在的 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, 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, 0, 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, start, 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);