-
-
Save p4535992/ea2d7a9dd19791744e3fb8b2be060a98 to your computer and use it in GitHub Desktop.
Loader for loading embedded assemblies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.InteropServices; | |
namespace Utility | |
{ | |
public class Loader | |
{ | |
const string LibsFolder = "Libs"; | |
static readonly Dictionary<string, Assembly> Libraries = new Dictionary<string, Assembly>(); | |
static readonly Dictionary<string, Assembly> ReflectionOnlyLibraries = new Dictionary<string, Assembly>(); | |
public static void Start() | |
{ | |
AppDomain.CurrentDomain.AssemblyResolve += FindAssembly; | |
PreloadUnmanagedLibraries(); | |
var app = new App(); | |
app.Run(); | |
} | |
[DllImport("kernel32.dll")] | |
private static extern IntPtr LoadLibrary(string dllToLoad); | |
private static void PreloadUnmanagedLibraries() | |
{ | |
// Preload correct library | |
var bittyness = "x86"; | |
if (IntPtr.Size == 8) | |
bittyness = "x64"; | |
var assemblyName = Assembly.GetExecutingAssembly().GetName(); | |
var libraries = Assembly.GetExecutingAssembly().GetManifestResourceNames() | |
.Where(s => s.StartsWith(String.Format("{1}.{2}.{0}.", bittyness, assemblyName.Name, LibsFolder))) | |
.ToArray(); | |
var dirName = Path.Combine(Path.GetTempPath(), String.Format("{2}.{1}.{0}", assemblyName.Version, bittyness, assemblyName.Name)); | |
if (!Directory.Exists(dirName)) | |
Directory.CreateDirectory(dirName); | |
foreach (var lib in libraries) | |
{ | |
string dllPath = Path.Combine(dirName, String.Join(".", lib.Split('.').Skip(3))); | |
if (!File.Exists(dllPath)) | |
{ | |
using (Stream stm = Assembly.GetExecutingAssembly().GetManifestResourceStream(lib)) | |
{ | |
// Copy the assembly to the temporary file | |
try | |
{ | |
using (Stream outFile = File.Create(dllPath)) | |
{ | |
stm.CopyTo(outFile); | |
} | |
} | |
catch | |
{ | |
// This may happen if another process has already created and loaded the file. | |
// Since the directory includes the version number of this assembly we can | |
// assume that it's the same bits, so we just ignore the excecption here and | |
// load the DLL. | |
} | |
} | |
} | |
// We must explicitly load the DLL here because the temporary directory | |
// is not in the PATH. | |
// Once it is loaded, the DllImport directives that use the DLL will use | |
// the one that is already loaded into the process. | |
LoadLibrary(dllPath); | |
} | |
} | |
internal static Assembly LoadAssembly(string fullName) | |
{ | |
Assembly a; | |
var executingAssembly = Assembly.GetExecutingAssembly(); | |
var assemblyName = executingAssembly.GetName(); | |
var shortName = new AssemblyName(fullName).Name; | |
if (Libraries.ContainsKey(shortName)) | |
return Libraries[shortName]; | |
var resourceName = String.Format("{0}.{2}.{1}.dll", assemblyName.Name, shortName, LibsFolder); | |
var actualName = executingAssembly.GetManifestResourceNames().FirstOrDefault(n => string.Equals(n, resourceName, StringComparison.OrdinalIgnoreCase)); | |
if (string.IsNullOrEmpty(actualName)) | |
{ | |
// The library might be a mixed mode assembly. Try loading from the bitty folders. | |
var bittyness = "x86"; | |
if (IntPtr.Size == 8) | |
bittyness = "x64"; | |
resourceName = String.Format("{0}.{3}.{1}.{2}.dll", assemblyName.Name, bittyness, shortName, LibsFolder); | |
actualName = executingAssembly.GetManifestResourceNames().FirstOrDefault(n => string.Equals(n, resourceName, StringComparison.OrdinalIgnoreCase)); | |
if (string.IsNullOrEmpty(actualName)) | |
{ | |
Libraries[shortName] = null; | |
return null; | |
} | |
// Ok, mixed mode assemblies cannot be loaded through Assembly.Load. | |
// See http://stackoverflow.com/questions/2945080/ and http://connect.microsoft.com/VisualStudio/feedback/details/97801/ | |
// But, since it's an unmanaged library we've already dumped it to disk to preload it into the process. | |
// So, we'll just load it from there. | |
var dirName = Path.Combine(Path.GetTempPath(), String.Format("{2}.{1}.{0}", assemblyName.Version, bittyness, assemblyName.Name)); | |
var dllPath = Path.Combine(dirName, String.Join(".", actualName.Split('.').Skip(3))); | |
if (!File.Exists(dllPath)) | |
{ | |
Libraries[shortName] = null; | |
return null; | |
} | |
a = Assembly.LoadFile(dllPath); | |
Libraries[shortName] = a; | |
return a; | |
} | |
using (var s = executingAssembly.GetManifestResourceStream(actualName)) | |
{ | |
var data = new BinaryReader(s).ReadBytes((int)s.Length); | |
byte[] debugData = null; | |
if (executingAssembly.GetManifestResourceNames().Contains(String.Format("{0}.{2}.{1}.pdb", assemblyName.Name, shortName, LibsFolder))) | |
{ | |
using (var ds = executingAssembly.GetManifestResourceStream(String.Format("{0}.{2}.{1}.pdb", assemblyName.Name, shortName, LibsFolder))) | |
{ | |
debugData = new BinaryReader(ds).ReadBytes((int)ds.Length); | |
} | |
} | |
if (debugData != null) | |
{ | |
a = Assembly.Load(data, debugData); | |
Libraries[shortName] = a; | |
return a; | |
} | |
a = Assembly.Load(data); | |
Libraries[shortName] = a; | |
return a; | |
} | |
} | |
internal static Assembly ReflectionOnlyLoadAssembly(string fullName) | |
{ | |
var executingAssembly = Assembly.GetExecutingAssembly(); | |
var assemblyName = Assembly.GetExecutingAssembly().GetName(); | |
string shortName = new AssemblyName(fullName).Name; | |
if (ReflectionOnlyLibraries.ContainsKey(shortName)) | |
return ReflectionOnlyLibraries[shortName]; | |
var resourceName = String.Format("{0}.{2}.{1}.dll", assemblyName.Name, shortName, LibsFolder); | |
if (!executingAssembly.GetManifestResourceNames().Contains(resourceName)) | |
{ | |
ReflectionOnlyLibraries[shortName] = null; | |
return null; | |
} | |
using (var s = executingAssembly.GetManifestResourceStream(resourceName)) | |
{ | |
var data = new BinaryReader(s).ReadBytes((int)s.Length); | |
var a = Assembly.ReflectionOnlyLoad(data); | |
ReflectionOnlyLibraries[shortName] = a; | |
return a; | |
} | |
} | |
internal static Assembly FindAssembly(object sender, ResolveEventArgs args) | |
{ | |
return LoadAssembly(args.Name); | |
} | |
internal static Assembly FindReflectionOnlyAssembly(object sender, ResolveEventArgs args) | |
{ | |
return ReflectionOnlyLoadAssembly(args.Name); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment