118 lines
5.1 KiB
C#
118 lines
5.1 KiB
C#
|
using System;
|
|||
|
using System.Runtime.ConstrainedExecution;
|
|||
|
using System.Runtime.InteropServices;
|
|||
|
using System.Security.Permissions;
|
|||
|
using Microsoft.Win32.SafeHandles;
|
|||
|
|
|||
|
// Source: https://docs.microsoft.com/en-us/archive/blogs/jmstall/type-safe-managed-wrappers-for-kernel32getprocaddress
|
|||
|
namespace SimConnect.Lib
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Utility class to wrap an unmanaged DLL and be responsible for freeing it.
|
|||
|
/// </summary>
|
|||
|
/// <remarks>
|
|||
|
/// This is a managed wrapper over the native LoadLibrary, GetProcAddress, and FreeLibrary calls.
|
|||
|
/// </remarks>
|
|||
|
public sealed class UnmanagedLibrary : IDisposable
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// See http://msdn.microsoft.com/msdnmag/issues/05/10/Reliability/ for more about safe handles.
|
|||
|
/// </summary>
|
|||
|
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
|
|||
|
public sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
|
|||
|
{
|
|||
|
private SafeLibraryHandle() : base(true) { }
|
|||
|
|
|||
|
/// <inheritdoc />
|
|||
|
protected override bool ReleaseHandle()
|
|||
|
{
|
|||
|
return NativeMethods.FreeLibrary(handle);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static class NativeMethods
|
|||
|
{
|
|||
|
private const string SKernel = "kernel32";
|
|||
|
|
|||
|
[DllImport(SKernel, CharSet = CharSet.Auto, BestFitMapping = false, SetLastError = true)]
|
|||
|
public static extern SafeLibraryHandle LoadLibrary(string fileName);
|
|||
|
|
|||
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|||
|
[DllImport(SKernel, SetLastError = true)]
|
|||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|||
|
public static extern bool FreeLibrary(IntPtr hModule);
|
|||
|
|
|||
|
[DllImport(SKernel)]
|
|||
|
public static extern IntPtr GetProcAddress(SafeLibraryHandle hModule, String procname);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Constructor to load a dll and be responible for freeing it.
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileName">full path name of dll to load</param>
|
|||
|
/// <exception cref="System.IO.FileNotFoundException">if fileName can't be found</exception>
|
|||
|
/// <remarks>Throws exceptions on failure. Most common failure would be file-not-found, or
|
|||
|
/// that the file is not a loadable image.</remarks>
|
|||
|
public UnmanagedLibrary(string fileName)
|
|||
|
{
|
|||
|
libraryHandle = NativeMethods.LoadLibrary(fileName);
|
|||
|
if (!libraryHandle.IsInvalid)
|
|||
|
return;
|
|||
|
|
|||
|
var hr = Marshal.GetHRForLastWin32Error();
|
|||
|
Marshal.ThrowExceptionForHR(hr);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Dynamically lookup a function in the dll via kernel32!GetProcAddress.
|
|||
|
/// </summary>
|
|||
|
/// <param name="functionName">raw name of the function in the export table.</param>
|
|||
|
/// <returns>null if function is not found. Else a delegate to the unmanaged function.
|
|||
|
/// </returns>
|
|||
|
/// <remarks>GetProcAddress results are valid as long as the dll is not yet unloaded. This
|
|||
|
/// is very very dangerous to use since you need to ensure that the dll is not unloaded
|
|||
|
/// until after you're done with any objects implemented by the dll. For example, if you
|
|||
|
/// get a delegate that then gets an IUnknown implemented by this dll,
|
|||
|
/// you can not dispose this library until that IUnknown is collected. Else, you may free
|
|||
|
/// the library and then the CLR may call release on that IUnknown and it will crash.</remarks>
|
|||
|
public TDelegate GetUnmanagedFunction<TDelegate>(string functionName) where TDelegate : class
|
|||
|
{
|
|||
|
var p = NativeMethods.GetProcAddress(libraryHandle, functionName);
|
|||
|
|
|||
|
// Failure is a common case, especially for adaptive code.
|
|||
|
if (p == IntPtr.Zero)
|
|||
|
return null;
|
|||
|
|
|||
|
var function = Marshal.GetDelegateForFunctionPointer(p, typeof(TDelegate));
|
|||
|
|
|||
|
// Ideally, we'd just make the constraint on TDelegate be
|
|||
|
// System.Delegate, but compiler error CS0702 (constrained can't be System.Delegate)
|
|||
|
// prevents that. So we make the constraint system.object and do the cast from object-->TDelegate.
|
|||
|
object o = function;
|
|||
|
|
|||
|
return (TDelegate)o;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Call FreeLibrary on the unmanaged dll. All function pointers
|
|||
|
/// handed out from this class become invalid after this.
|
|||
|
/// </summary>
|
|||
|
/// <remarks>This is very dangerous because it suddenly invalidate
|
|||
|
/// everything retrieved from this dll. This includes any functions
|
|||
|
/// handed out via GetProcAddress, and potentially any objects returned
|
|||
|
/// from those functions (which may have an implemention in the
|
|||
|
/// dll).
|
|||
|
/// </remarks>
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
if (!libraryHandle.IsClosed)
|
|||
|
{
|
|||
|
libraryHandle.Close();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Unmanaged resource. CLR will ensure SafeHandles get freed, without requiring a finalizer on this class.
|
|||
|
private readonly SafeLibraryHandle libraryHandle;
|
|||
|
}
|
|||
|
}
|