// --------------------------------------------------------------------------------------------------------------------
//
// 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
}
}
}
}