diff --git a/src/Squirrel/NativeMethods.cs b/src/Squirrel/NativeMethods.cs new file mode 100644 index 00000000..611f4d3b --- /dev/null +++ b/src/Squirrel/NativeMethods.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Squirrel +{ + static class NativeMethods + { + [DllImport("version.dll", SetLastError = true)] + public static extern bool GetFileVersionInfo( + string lpszFileName, + IntPtr dwHandleIgnored, + int dwLen, + [MarshalAs(UnmanagedType.LPArray)] byte[] lpData); + + [DllImport("version.dll", SetLastError = true)] + public static extern int GetFileVersionInfoSize( + string lpszFileName, + IntPtr dwHandleIgnored); + + [DllImport("version.dll")] + public static extern bool VerQueryValue(byte[] pBlock, string pSubBlock, out IntPtr pValue, out int len); + } +} diff --git a/src/Squirrel/Squirrel.csproj b/src/Squirrel/Squirrel.csproj index 1adfd660..48ab0205 100644 --- a/src/Squirrel/Squirrel.csproj +++ b/src/Squirrel/Squirrel.csproj @@ -71,12 +71,14 @@ + + diff --git a/src/Squirrel/SquirrelAwareExecutableDetector.cs b/src/Squirrel/SquirrelAwareExecutableDetector.cs new file mode 100644 index 00000000..a99d8d22 --- /dev/null +++ b/src/Squirrel/SquirrelAwareExecutableDetector.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Squirrel +{ + static class SquirrelAwareExecutableDetector + { + public static int? GetPESquirrelAwareVersion(string executable) + { + if (!File.Exists(executable)) return null; + var fullname = Path.GetFullPath(executable); + + return GetAssemblySquirrelAwareVersion(fullname) ?? GetVersionBlockSquirrelAwareValue(fullname); + } + + static int? GetAssemblySquirrelAwareVersion(string executable) + { + try + { + var assembly = Assembly.ReflectionOnlyLoadFrom(executable); + var attrs = assembly.GetCustomAttributesData(); + var attribute = attrs.FirstOrDefault(x => + { + if (x.AttributeType != typeof(AssemblyMetadataAttribute)) return false; + if (x.ConstructorArguments.Count != 2) return false; + return x.ConstructorArguments[0].Value.ToString() == "SquirrelAwareVersion"; + }); + + if (attribute == null) return null; + + int result; + if (!Int32.TryParse(attribute.ConstructorArguments[1].Value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture, out result)) + { + return null; + } + + return result; + } + catch (FileLoadException) { return null; } + catch (BadImageFormatException) { return null; } + } + + static int? GetVersionBlockSquirrelAwareValue(string executable) + { + int size = NativeMethods.GetFileVersionInfoSize(executable, IntPtr.Zero); + + // Nice try, buffer overflow + if (size <= 0 || size > 4096) return null; + + var buf = new byte[size]; + if (!NativeMethods.GetFileVersionInfo(executable, IntPtr.Zero, size, buf)) return null; + + IntPtr result; int resultSize; + if (!NativeMethods.VerQueryValue(buf, "\\StringFileInfo\\040904B0\\SquirrelAwareVersion", out result, out resultSize)) + { + return null; + } + + int ret; + string resultData = Marshal.PtrToStringAnsi(result, resultSize-1 /* Subtract one for null terminator */); + if (!Int32.TryParse(resultData, NumberStyles.Integer, CultureInfo.CurrentCulture, out ret)) return null; + + return ret; + } + } +}