bovender framework
C# framework that implements MVVM and more
DllFile.cs
1 /* DllRecord.cs
2  * part of Bovender framework
3  *
4  * Copyright 2014-2018 Daniel Kraus
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 using System;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Linq;
22 using System.Runtime.InteropServices;
23 using System.Text;
24 
25 namespace Bovender.Unmanaged
26 {
30  class DllFile : IDisposable
31  {
32  #region Properties
33 
34  public string DllPath { get; private set; }
35 
36  public IntPtr Handle { get; private set; }
37 
38  public int UseCount { get; private set; }
39 
40  #endregion
41 
42  #region Public methods
43 
44  public bool Load()
45  {
46  Logger.Info("Load: Use count of '{0}' was {1}", DllPath, UseCount);
47  bool result = false;
48  if (UseCount == 0)
49  {
50  Logger.Info("Load: Loading DLL...");
51 
52  if (!String.IsNullOrWhiteSpace(_expectedSha256))
53  {
54  if (!VerifyChecksum())
55  {
56  Logger.Fatal("LoadDll: Checksum mismatch!");
57  throw new DllSha1MismatchException(String.Format(
58  "DLL checksum error: expected {0} on {1}", _expectedSha256, DllPath));
59  }
60  };
61 
62  Handle = LoadLibrary(DllPath);
63 
64  if (Handle == IntPtr.Zero)
65  {
66  Logger.Fatal("Load: Unable to load DLL!");
67  Win32Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
68  Logger.Fatal(inner);
69  throw new DllLoadingFailedException(
70  String.Format(
71  "Could not load DLL file: LoadLibrary failed with code {0} on {1}",
72  Marshal.GetLastWin32Error(), SanitizeDllPath()
73  ),
74  inner
75  );
76  }
77  else
78  {
79  Logger.Info("Load: Handle: 0x{0}", Handle.ToString("X8"));
80  }
81  }
82 
83  UseCount++;
84  return result;
85  }
86 
87  public void Unload()
88  {
89  Logger.Info("Unload: Use count for '{0}' is {1}", DllPath, UseCount);
90  if (UseCount <= 0)
91  {
92  Logger.Warn("Unload: Method call not matched by Load() call!");
93  }
94  else
95  {
96  UseCount--;
97  }
98  if (UseCount == 0)
99  {
100  Logger.Info("Unload: No more users, freeing handle 0x{0}", Handle.ToString("X8"));
101  if (!FreeLibrary(Handle))
102  {
103  Logger.Warn("Unload: FreeLibrary returned false");
104  }
105  }
106  }
107 
108  #endregion
109 
110  #region Constructors
111 
112  public DllFile(string path)
113  {
114  Logger.Info("Constructor: path: {0}", path);
115  DllPath = path;
116  UseCount = 0;
117  Handle = IntPtr.Zero;
118  }
119 
120  public DllFile(string path, string sha256hash)
121  : this(path)
122  {
123  _expectedSha256 = sha256hash;
124  }
125 
126  #endregion
127 
128  #region Disposing
129 
130  ~DllFile()
131  {
132  Dispose(false);
133  }
134 
135  public void Dispose()
136  {
137  Dispose(true);
138  GC.SuppressFinalize(this);
139  }
140 
141  private void Dispose(bool disposing)
142  {
143  if (!_disposed)
144  {
145  _disposed = true;
146  if (UseCount > 0)
147  {
148  FreeLibrary(Handle);
149  }
150  UseCount = 0;
151  }
152  }
153 
154  #endregion
155 
156  #region Private methods
157 
163  private string SanitizeDllPath()
164  {
165  // Strip the leading directories from the path info (they may contain
166  // sensitive information about where exactly a user has installed files).
167  string[] dirs = System.IO.Path.GetDirectoryName(DllPath).Split(System.IO.Path.DirectorySeparatorChar);
168  string result = DllPath;
169  int n = dirs.Length;
170  if (n > 0) result = System.IO.Path.Combine(dirs[n - 1], result);
171  if (n > 1) result = System.IO.Path.Combine(dirs[n - 2], result);
172  if (n > 2) result = System.IO.Path.Combine("...", result);
173  Logger.Info("SanitizeDllPath: {0}", result);
174  return result;
175  }
176 
177  private bool VerifyChecksum()
178  {
179  string actual = FileHelpers.Sha256Hash(DllPath);
180  if (actual != _expectedSha256)
181  {
182  Logger.Warn("VerifyChecksum: Checksum failed for '{0}'", DllPath);
183  Logger.Warn("VerifyChecksum: Expected: {0}", _expectedSha256);
184  Logger.Warn("VerifyChecksum: Actual: {0}", actual);
185  return false;
186  }
187  else
188  {
189  Logger.Info("VerifyChecksum: Confirmed: {0}", _expectedSha256);
190  return true;
191  }
192  }
193 
194  #endregion
195 
196  #region Private fields
197 
198  private bool _disposed;
199  private string _expectedSha256;
200 
201  #endregion
202 
203  #region WinAPI
204 
205  [DllImport("kernel32.dll", EntryPoint = "LoadLibrary", SetLastError = true)]
206  static extern IntPtr LoadLibrary(string dllToLoad);
207 
208  [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", SetLastError = true)]
209  static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
210 
211  [DllImport("kernel32.dll", EntryPoint = "FreeLibrary", SetLastError = true)]
212  static extern bool FreeLibrary(IntPtr hModule);
213 
214  #endregion
215 
216  #region Class logger
217 
218  private static NLog.Logger Logger { get { return _logger.Value; } }
219 
220  private static readonly Lazy<NLog.Logger> _logger = new Lazy<NLog.Logger>(() => NLog.LogManager.GetCurrentClassLogger());
221 
222  #endregion
223  }
224 }
static string Sha256Hash(string file)
Computes the Sha256 hash of a given file.
Definition: FileHelpers.cs:36
Represents a single DLL file.
Definition: DllFile.cs:30