bingo/sqlserver/Source/BingoDllLoader.cs (310 lines of code) (raw):
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Resources;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
namespace indigo
{
class LibraryLoader
{
class WindowsLoader
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll")]
public static extern int FreeLibrary(IntPtr module);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern int GetLastError();
}
public static IntPtr LoadLibrary(string filename)
{
return WindowsLoader.LoadLibrary(filename);
}
public static int FreeLibrary(IntPtr handle)
{
return WindowsLoader.FreeLibrary(handle);
}
public static IntPtr GetProcAddress(IntPtr library, string procedureName)
{
return WindowsLoader.GetProcAddress(library, procedureName);
}
public static string GetLastError()
{
return WindowsLoader.GetLastError().ToString();
}
}
// Singleton DLL loader
public class BingoDllLoader
{
private static volatile BingoDllLoader _instance;
private static object _global_sync_root = new Object();
private static volatile int _instance_id = 0;
public static BingoDllLoader Instance
{
get
{
if (_instance == null)
{
lock (_global_sync_root)
{
if (_instance == null)
_instance = new BingoDllLoader();
}
}
return _instance;
}
}
// Returns Id of the instance. When DllLoader is being finalized this value gets increased.
public static int InstanceId
{
get
{
return _instance_id;
}
}
class WrappedInterface
{
// Dictionary with delegates for calling unmanaged functions
public Dictionary<string, Delegate> delegates = new Dictionary<string, Delegate>();
// Interface instance with wrappers for calling unmanaged functions
public object instance = null;
}
class DllData
{
public IntPtr handle;
public string file_name;
public string lib_path;
public Dictionary<Type, WrappedInterface> interfaces = new Dictionary<Type, WrappedInterface>();
}
// Mapping from the DLL name to the handle.
Dictionary<string, DllData> _loaded_dlls = new Dictionary<string, DllData>();
// DLL handles in the loading order
List<DllData> _dll_handles = new List<DllData>();
// Local synchronization object
Object _sync_object = new Object();
public void loadLibrary(String path, String dll_name, string resource_name, bool make_unique_dll_name)
{
lock (_sync_object)
{
DllData data = null;
if (_loaded_dlls.TryGetValue(dll_name, out data))
{
// Library has already been loaded
if (data.lib_path != path)
throw new Exception(String.Format("Library {0} has already been loaded by different path {1}", dll_name, data.lib_path));
return;
}
data = new DllData();
data.lib_path = path;
data.file_name = _getPathToBinary(path, dll_name, resource_name, Assembly.GetCallingAssembly(), make_unique_dll_name);
data.file_name = data.file_name.Replace('/', '\\');
data.handle = LibraryLoader.LoadLibrary(data.file_name);
if (data.handle == IntPtr.Zero)
throw new Exception("Cannot load library " + dll_name +
" from the temporary file " + data.file_name.Replace('\\', '/') + ": " + LibraryLoader.GetLastError()
);
_loaded_dlls.Add(dll_name, data);
_dll_handles.Add(data);
}
}
~BingoDllLoader()
{
lock (_global_sync_root)
{
_instance = null;
_instance_id++;
// Unload all loaded libraries in the reverse order
_dll_handles.Reverse();
foreach (DllData dll in _dll_handles)
LibraryLoader.FreeLibrary(dll.handle);
}
}
public bool isValid()
{
return (_instance != null);
}
string _getPathToBinary(String path, String filename, String resource_name,
Assembly resource_assembly, bool make_unique_dll_name)
{
if (path == null)
return _extractFromAssembly(filename, resource_name, resource_assembly, make_unique_dll_name);
return Path.Combine(path, filename);
}
String _getTemporaryDirectory(Assembly resource_assembly)
{
String dir;
dir = Path.Combine(Path.GetTempPath(), "EPAM_bingo");
dir = Path.Combine(dir, resource_assembly.GetName().Name);
dir = Path.Combine(dir, resource_assembly.GetName().Version.ToString());
return dir;
}
String _extractFromAssembly(String filename, String resource_name,
Assembly resource_assembly, bool make_unique_dll_name)
{
ResourceManager manager = new ResourceManager(resource_name, resource_assembly);
String output = String.Join(" ", resource_assembly.GetManifestResourceNames());
Object file_data = manager.GetObject(filename);
if (file_data == null)
throw new Exception("Internal error: there is no resource " + filename + " in resource " + resource_name + ". All resources: " + output);
String tmpdir_path = _getTemporaryDirectory(resource_assembly);
String version = resource_assembly.GetName().Version.ToString();
// Make per-version-unique dependent dll name
String path = Path.Combine(tmpdir_path, filename);
String dir = Path.GetDirectoryName(path);
String name = Path.GetFileName(path);
String new_dll_name;
if (make_unique_dll_name)
new_dll_name = version + "_" + name;
else
new_dll_name = name;
// This temporary file is used to avoid inter-process
// race condition when concurrently stating many processes
// on the same machine for the first time.
String tmp_filename = Path.GetTempFileName();
String new_full_path = Path.Combine(dir, new_dll_name);
FileInfo file = new FileInfo(new_full_path);
file.Directory.Create();
// Check if file already exists
if (!file.Exists || file.Length == 0) {
File.WriteAllBytes(tmp_filename, (byte[]) file_data);
// file is ready to be moved.. lets check again
if (!file.Exists || file.Length == 0) {
File.Move(tmp_filename, file.FullName);
} else {
File.Delete(tmp_filename);
}
}
return file.FullName;
}
// Returns implementation of a given interface for wrapping function the specified DLL
public IT getInterface<IT>(string dll_name) where IT : class
{
lock (_sync_object)
{
Type itype = typeof(IT);
// Check if such interface was already loaded
WrappedInterface interf = null;
if (_loaded_dlls.ContainsKey(dll_name))
{
if (!_loaded_dlls[dll_name].interfaces.TryGetValue(itype, out interf))
{
interf = createInterface<IT>(dll_name);
_loaded_dlls[dll_name].interfaces.Add(itype, interf);
}
}
else
{
interf = createInterface<IT>(dll_name);
_loaded_dlls[dll_name].interfaces.Add(itype, interf);
}
return (IT)interf.instance;
}
}
string getDelegateField(MethodInfo m)
{
return m.Name + "_ptr";
}
Type createDelegateType(string delegate_type_name, ModuleBuilder mb, Type ret_type, Type[] arg_types)
{
// Create delegate
TypeBuilder delegate_type = mb.DefineType(delegate_type_name,
TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed |
TypeAttributes.AnsiClass | TypeAttributes.AutoClass,
typeof(System.MulticastDelegate));
ConstructorBuilder constructorBuilder =
delegate_type.DefineConstructor(MethodAttributes.RTSpecialName |
MethodAttributes.HideBySig | MethodAttributes.Public,
CallingConventions.Standard,
new Type[] { typeof(object), typeof(System.IntPtr) });
constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
MethodBuilder methodBuilder = delegate_type.DefineMethod("Invoke",
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.NewSlot | MethodAttributes.Virtual,
ret_type, arg_types);
methodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
// Add [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute for the delegate
ConstructorInfo func_pointer_constructor =
typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new Type[] { typeof(CallingConvention) });
CustomAttributeBuilder ca_builder =
new CustomAttributeBuilder(func_pointer_constructor, new object[] { CallingConvention.Cdecl });
delegate_type.SetCustomAttribute(ca_builder);
return delegate_type.CreateType();
}
private class TypeListComparer : IEqualityComparer<List<Type>>
{
public bool Equals(List<Type> x, List<Type> y)
{
if (x.Count != y.Count)
return false;
for (int i = 0; i < x.Count; i++)
if (x[i] != y[i])
return false;
return true;
}
public int GetHashCode(List<Type> obj)
{
int hash = 0;
foreach (Type t in obj)
hash ^= t.GetHashCode();
return hash;
}
}
// Creates implementation of a given interface for wrapping function the specified DLL
WrappedInterface createInterface<IT>(string dll_name) where IT : class
{
WrappedInterface result = new WrappedInterface();
Type itype = typeof(IT);
AppDomain cd = System.Threading.Thread.GetDomain();
AssemblyName an = new AssemblyName();
an.Name = itype.Name + "_" + dll_name.Replace('.', '_');
AssemblyBuilder ab = cd.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder mb = ab.DefineDynamicModule(an.Name, false);
TypeBuilder tb = mb.DefineType(an.Name, TypeAttributes.Class |
TypeAttributes.Public);
tb.AddInterfaceImplementation(itype);
IntPtr dll_handle = _loaded_dlls[dll_name].handle;
Dictionary<List<Type>, Type> signature_to_name =
new Dictionary<List<Type>, Type>(new TypeListComparer());
// Set delegate references
foreach (MethodInfo m in itype.GetMethods())
{
ParameterInfo[] parameters = m.GetParameters();
Type[] arg_types = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
arg_types[i] = parameters[i].ParameterType;
Type delegate_ret_type = m.ReturnType;
if (delegate_ret_type == typeof(String))
delegate_ret_type = typeof(sbyte*);
List<Type> signature = new List<Type>();
signature.Add(delegate_ret_type);
signature.AddRange(arg_types);
Type call_delegate = null;
if (!signature_to_name.TryGetValue(signature, out call_delegate))
{
// Check if type was already created
string delegate_type_name = String.Format("delegate_{0}", signature_to_name.Count);
call_delegate = createDelegateType(delegate_type_name, mb, delegate_ret_type, arg_types);
signature_to_name.Add(signature, call_delegate);
}
string delegate_field_name = m.Name + "_ptr";
FieldBuilder delegate_field =
tb.DefineField(delegate_field_name, typeof(Delegate), FieldAttributes.Private);
IntPtr proc = LibraryLoader.GetProcAddress(dll_handle, m.Name);
if (proc == IntPtr.Zero)
throw new Exception(String.Format("Cannot find procedure {0} in the library {1}", m.Name, dll_name));
Delegate proc_delegate = Marshal.GetDelegateForFunctionPointer(proc, call_delegate);
result.delegates.Add(delegate_field_name, proc_delegate);
MethodBuilder meth = tb.DefineMethod(m.Name,
MethodAttributes.Public | MethodAttributes.Virtual, m.ReturnType, arg_types);
ILGenerator il = meth.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, delegate_field);
for (int i = 1; i < arg_types.Length + 1; i++)
il.Emit(OpCodes.Ldarg, i);
MethodInfo infoMethod = proc_delegate.GetType().GetMethod("Invoke", arg_types);
il.EmitCall(OpCodes.Callvirt, infoMethod, null);
// Automatically convert sbyte* to String
if (m.ReturnType == typeof(String))
{
Type str_type = typeof(String);
ConstructorInfo ci = str_type.GetConstructor(new Type[] { typeof(sbyte*) });
il.Emit(OpCodes.Newobj, ci);
}
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(meth, m);
}
// ab.Save(an.Name + ".dll");
Type impl_class = tb.CreateType();
IT impl = (IT)Activator.CreateInstance(impl_class);
// Set references to the delegates
foreach (string field_name in result.delegates.Keys)
{
impl_class.GetField(field_name, BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(impl, result.delegates[field_name]);
}
result.instance = impl;
return result;
}
}
}