/Squared/Util/EmbeddedDLLLoader.cs

http://github.com/kevingadd/Fracture · C# · 121 lines · 103 code · 17 blank · 1 comment · 18 complexity · c73ea7467d7ee7c05be3acbc97bb6fb7 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace Squared.Util {
  10. public class EmbeddedDLLLoader : IDisposable {
  11. public bool IsDisposed { get; private set; }
  12. [DllImport("kernel32", SetLastError=true, CharSet = CharSet.Ansi)]
  13. static extern IntPtr LoadLibrary(
  14. [MarshalAs(UnmanagedType.LPStr)]
  15. string lpFileName
  16. );
  17. [DllImport("kernel32", SetLastError=true)]
  18. static extern bool FreeLibrary(IntPtr hModule);
  19. public readonly Assembly Assembly;
  20. internal readonly List<IntPtr> LoadedHandles = new List<IntPtr>();
  21. internal readonly List<string> CreatedFiles = new List<string>();
  22. internal string TemporaryDirectory;
  23. internal bool IsRegisteredForShutdownNotice;
  24. public EmbeddedDLLLoader (Assembly assembly) {
  25. Assembly = assembly;
  26. }
  27. private string GetDirectory () {
  28. if (TemporaryDirectory == null) {
  29. var tempdir = Path.GetTempPath();
  30. if (tempdir != null) {
  31. var desiredPath = Path.Combine(tempdir, "Squared.Util.EmbeddedDLLLoader", Assembly.GetName().Name);
  32. try {
  33. Directory.CreateDirectory(desiredPath);
  34. return TemporaryDirectory = desiredPath;
  35. } catch {
  36. }
  37. }
  38. var path = Path.GetTempFileName();
  39. File.Delete(path);
  40. Directory.CreateDirectory(path);
  41. TemporaryDirectory = path;
  42. }
  43. return TemporaryDirectory;
  44. }
  45. private static bool StreamsMatch (Stream a, Stream b) {
  46. if (a.Length != b.Length)
  47. return false;
  48. const int chunkSize = 81920;
  49. var bufA = new byte[chunkSize];
  50. var bufB = new byte[chunkSize];
  51. for (int i = 0, l = (int)a.Length; i < l; i += chunkSize) {
  52. var bytesReadA = a.Read(bufA, 0, chunkSize);
  53. var bytesReadB = b.Read(bufB, 0, chunkSize);
  54. if (bytesReadA != bytesReadB)
  55. return false;
  56. for (int j = 0; j < bytesReadA; j++)
  57. if (bufA[j] != bufB[j])
  58. return false;
  59. }
  60. return true;
  61. }
  62. public void Load (string name) {
  63. var path = Path.Combine(GetDirectory(), name);
  64. using (var src = Assembly.GetManifestResourceStream(name)) {
  65. try {
  66. var pdb = Assembly.GetManifestResourceStream(name.Replace(".dll", ".pdb"));
  67. if (pdb != null) {
  68. using (pdb)
  69. using (var dest = File.OpenWrite(path.Replace(".dll", ".pdb")))
  70. pdb.CopyTo(dest);
  71. }
  72. } catch {
  73. }
  74. try {
  75. using (var dest = File.OpenWrite(path))
  76. src.CopyTo(dest);
  77. } catch (IOException exc) {
  78. // File is locked. Attempt to see if it matches ours and if so, who cares
  79. using (var existing = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
  80. if (!StreamsMatch(src, existing))
  81. throw new IOException("Could not unpack DLL and the existing one does not match");
  82. }
  83. }
  84. CreatedFiles.Add(path);
  85. var hLibrary = LoadLibrary(path);
  86. if (hLibrary == null)
  87. throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error(), "Failed to load " + name);
  88. LoadedHandles.Add(hLibrary);
  89. }
  90. public void Dispose () {
  91. if (IsDisposed)
  92. return;
  93. IsDisposed = true;
  94. foreach (var ptr in LoadedHandles)
  95. FreeLibrary(ptr);
  96. foreach (var file in CreatedFiles) {
  97. try {
  98. File.Delete(file);
  99. } catch {
  100. }
  101. }
  102. }
  103. }
  104. }