BetterDiscordApp-v1/Installers/dotNet2/BetterDiscordWI/BetterDiscordWI/obj/Release/Common.cs

297 lines
9.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Threading;
static class Common
{
private const int DelayUntilReboot = 4;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, int dwFlags);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetDllDirectory(string lpPathName);
[Conditional("DEBUG")]
public static void Log(string format, params object[] args)
{
// Should this be trace?
Debug.WriteLine("=== COSTURA === " + string.Format(format, args));
}
static void CopyTo(Stream source, Stream destination)
{
var array = new byte[81920];
int count;
while ((count = source.Read(array, 0, array.Length)) != 0)
{
destination.Write(array, 0, count);
}
}
static void CreateDirectory(string tempBasePath)
{
if (!Directory.Exists(tempBasePath))
{
Directory.CreateDirectory(tempBasePath);
}
}
static byte[] ReadStream(Stream stream)
{
var data = new Byte[stream.Length];
stream.Read(data, 0, data.Length);
return data;
}
public static string CalculateChecksum(string filename)
{
using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
using (var bs = new BufferedStream(fs))
using (var sha1 = new SHA1CryptoServiceProvider())
{
var hash = sha1.ComputeHash(bs);
var formatted = new StringBuilder(2 * hash.Length);
foreach (var b in hash)
{
formatted.AppendFormat("{0:X2}", b);
}
return formatted.ToString();
}
}
public static Assembly ReadExistingAssembly(AssemblyName name)
{
var currentDomain = AppDomain.CurrentDomain;
var assemblies = currentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var currentName = assembly.GetName();
if (string.Equals(currentName.Name, name.Name, StringComparison.InvariantCultureIgnoreCase) &&
string.Equals(CultureToString(currentName.CultureInfo), CultureToString(name.CultureInfo), StringComparison.InvariantCultureIgnoreCase))
{
Log("Assembly '{0}' already loaded, returning existing assembly", assembly.FullName);
return assembly;
}
}
return null;
}
static string CultureToString(CultureInfo culture)
{
if (culture == null)
return "";
return culture.Name;
}
public static Assembly ReadFromDiskCache(string tempBasePath, AssemblyName requestedAssemblyName)
{
var name = requestedAssemblyName.Name.ToLowerInvariant();
if (requestedAssemblyName.CultureInfo != null && !String.IsNullOrEmpty(requestedAssemblyName.CultureInfo.Name))
name = $"{requestedAssemblyName.CultureInfo.Name}.{name}";
var bittyness = IntPtr.Size == 8 ? "64" : "32";
var assemblyTempFilePath = Path.Combine(tempBasePath, String.Concat(name, ".dll"));
if (File.Exists(assemblyTempFilePath))
{
return Assembly.LoadFile(assemblyTempFilePath);
}
assemblyTempFilePath = Path.ChangeExtension(assemblyTempFilePath, "exe");
if (File.Exists(assemblyTempFilePath))
{
return Assembly.LoadFile(assemblyTempFilePath);
}
assemblyTempFilePath = Path.Combine(Path.Combine(tempBasePath, bittyness), String.Concat(name, ".dll"));
if (File.Exists(assemblyTempFilePath))
{
return Assembly.LoadFile(assemblyTempFilePath);
}
assemblyTempFilePath = Path.ChangeExtension(assemblyTempFilePath, "exe");
if (File.Exists(assemblyTempFilePath))
{
return Assembly.LoadFile(assemblyTempFilePath);
}
return null;
}
public static Assembly ReadFromEmbeddedResources(Dictionary<string, string> assemblyNames, Dictionary<string, string> symbolNames, AssemblyName requestedAssemblyName)
{
var name = requestedAssemblyName.Name.ToLowerInvariant();
if (requestedAssemblyName.CultureInfo != null && !String.IsNullOrEmpty(requestedAssemblyName.CultureInfo.Name))
name = $"{requestedAssemblyName.CultureInfo.Name}.{name}";
byte[] assemblyData;
using (var assemblyStream = LoadStream(assemblyNames, name))
{
if (assemblyStream == null)
{
return null;
}
assemblyData = ReadStream(assemblyStream);
}
using (var pdbStream = LoadStream(symbolNames, name))
{
if (pdbStream != null)
{
var pdbData = ReadStream(pdbStream);
return Assembly.Load(assemblyData, pdbData);
}
}
return Assembly.Load(assemblyData);
}
static Stream LoadStream(Dictionary<string, string> resourceNames, string name)
{
string value;
if (resourceNames.TryGetValue(name, out value))
return LoadStream(value);
return null;
}
static Stream LoadStream(string fullname)
{
var executingAssembly = Assembly.GetExecutingAssembly();
if (fullname.EndsWith(".compressed"))
{
using (var stream = executingAssembly.GetManifestResourceStream(fullname))
using (var compressStream = new DeflateStream(stream, CompressionMode.Decompress))
{
var memStream = new MemoryStream();
CopyTo(compressStream, memStream);
memStream.Position = 0;
return memStream;
}
}
return executingAssembly.GetManifestResourceStream(fullname);
}
// Mutex code from http://stackoverflow.com/questions/229565/what-is-a-good-pattern-for-using-a-global-mutex-in-c
public static void PreloadUnmanagedLibraries(string hash, string tempBasePath, IEnumerable<string> libs, Dictionary<string, string> checksums)
{
var mutexId = $"Global\\Costura{hash}";
using (var mutex = new Mutex(false, mutexId))
{
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
mutex.SetAccessControl(securitySettings);
var hasHandle = false;
try
{
try
{
hasHandle = mutex.WaitOne(60000, false);
if (hasHandle == false)
throw new TimeoutException("Timeout waiting for exclusive access");
}
catch (AbandonedMutexException)
{
hasHandle = true;
}
var bittyness = IntPtr.Size == 8 ? "64" : "32";
CreateDirectory(Path.Combine(tempBasePath, bittyness));
InternalPreloadUnmanagedLibraries(tempBasePath, libs, checksums);
}
finally
{
if (hasHandle)
mutex.ReleaseMutex();
}
}
}
static void InternalPreloadUnmanagedLibraries(string tempBasePath, IEnumerable<string> libs, Dictionary<string, string> checksums)
{
string name;
foreach (var lib in libs)
{
name = ResourceNameToPath(lib);
var assemblyTempFilePath = Path.Combine(tempBasePath, name);
if (File.Exists(assemblyTempFilePath))
{
var checksum = CalculateChecksum(assemblyTempFilePath);
if (checksum != checksums[lib])
File.Delete(assemblyTempFilePath);
}
if (!File.Exists(assemblyTempFilePath))
{
using (var copyStream = LoadStream(lib))
using (var assemblyTempFile = File.OpenWrite(assemblyTempFilePath))
{
CopyTo(copyStream, assemblyTempFile);
}
if (!MoveFileEx(assemblyTempFilePath, null, DelayUntilReboot))
{
//TODO: for now we ignore the return value.
}
}
}
SetDllDirectory(tempBasePath);
foreach (var lib in libs)
{
name = ResourceNameToPath(lib);
if (name.EndsWith(".dll"))
{
var assemblyTempFilePath = Path.Combine(tempBasePath, name);
LoadLibrary(assemblyTempFilePath);
}
}
}
static string ResourceNameToPath(string lib)
{
var bittyness = IntPtr.Size == 8 ? "64" : "32";
var name = lib;
if (lib.StartsWith(String.Concat("costura", bittyness, ".")))
{
name = Path.Combine(bittyness, lib.Substring(10));
}
else if (lib.StartsWith("costura."))
{
name = lib.Substring(8);
}
if (name.EndsWith(".compressed"))
{
name = name.Substring(0, name.Length - 11);
}
return name;
}
}