// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) James South. // Licensed under the Apache License, Version 2.0. // // // Controls the loading and unloading of any native binaries required by ImageProcessor. // // -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Configuration { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; /// /// Controls the loading and unloading of any native binaries required by ImageProcessor. /// public class NativeBinaryFactory : IDisposable { /// /// Whether the process is running in 64bit mode. Used for calling the correct dllimport method. /// private static readonly bool Is64Bit = Environment.Is64BitProcess; /// /// The native binaries. /// private static ConcurrentDictionary nativeBinaries; /// /// A value indicating whether this instance of the given entity has been disposed. /// /// if this instance has been disposed; otherwise, . /// /// If the entity is disposed, it must not be disposed a second /// time. The isDisposed field is set the first time the entity /// is disposed. If the isDisposed field is true, then the Dispose() /// method will not dispose again. This help not to prolong the entity's /// life in the Garbage Collector. /// private bool isDisposed; /// /// Initializes a new instance of the class. /// public NativeBinaryFactory() { nativeBinaries = new ConcurrentDictionary(); } /// /// Finalizes an instance of the ImageFactory class. /// /// /// Use C# destructor syntax for finalization code. /// This destructor will run only if the Dispose method /// does not get called. /// It gives your base class the opportunity to finalize. /// Do not provide destructors in types derived from this class. /// ~NativeBinaryFactory() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. this.Dispose(false); } /// /// Gets a value indicating whether the operating environment is 64 bit. /// public bool Is64BitEnvironment { get { return Is64Bit; } } /// /// Registers any embedded native (unmanaged) binaries required by ImageProcessor. /// /// /// The name of the native binary. /// /// /// The resource bytes containing the native binary. /// /// /// Thrown if the binary cannot be registered. /// public void RegisterNativeBinary(string name, byte[] resourceBytes) { nativeBinaries.GetOrAdd( name, b => { IntPtr pointer; string folder = Is64BitEnvironment ? "x64" : "x86"; Assembly assembly = Assembly.GetExecutingAssembly(); string targetBasePath = new Uri(assembly.Location).LocalPath; string targetPath = Path.GetFullPath(Path.Combine(targetBasePath, "..\\" + folder + "\\" + name)); // Copy the file across if necessary. FileInfo fileInfo = new FileInfo(targetPath); bool rewrite = true; if (fileInfo.Exists) { byte[] existing = File.ReadAllBytes(targetPath); if (resourceBytes.SequenceEqual(existing)) { rewrite = false; } } if (rewrite) { // ReSharper disable once AssignNullToNotNullAttribute DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(targetPath)); if (!directoryInfo.Exists) { directoryInfo.Create(); } File.WriteAllBytes(targetPath, resourceBytes); } try { #if !__MonoCS__ // Load the binary into memory. pointer = NativeMethods.LoadLibrary(targetPath); #else // Load the binary into memory. The second parameter forces it to load immediately. pointer = NativeMethods.dlopen(targetPath, 2); #endif } catch (Exception ex) { throw new ApplicationException(ex.Message); } if (pointer == IntPtr.Zero) { throw new ApplicationException("Cannot load " + name); } return pointer; }); } /// /// Disposes the object and frees resources for the Garbage Collector. /// public void Dispose() { this.Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } /// /// Disposes the object and frees resources for the Garbage Collector. /// /// If true, the object gets disposed. protected virtual void Dispose(bool disposing) { if (this.isDisposed) { return; } if (disposing) { // Dispose of any managed resources here. } // Call the appropriate methods to clean up // unmanaged resources here. this.FreeNativeBinaries(); // Note disposing is done. this.isDisposed = true; } /// /// Frees the reference to the native binaries. /// private void FreeNativeBinaries() { foreach (KeyValuePair nativeBinary in nativeBinaries) { IntPtr pointer = nativeBinary.Value; #if !__MonoCS__ // According to http://stackoverflow.com/a/2445558/427899 you need to call this twice. NativeMethods.FreeLibrary(pointer); NativeMethods.FreeLibrary(pointer); #else NativeMethods.dlclose(pointer); #endif } } } }