#region Copyright (c) Microsoft Corporation. // // The source code of UnmanagedLibrary.cs is quoted from Mike Stall's article: // // Type-safe Managed wrappers for kernel32!GetProcAddress // http://blogs.msdn.com/jmstall/archive/2007/01/06/Typesafe-GetProcAddress.aspx // // This source is subject to the Microsoft Public License. // See http://www.opensource.org/licenses/MS-PL. // All other rights reserved. // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, // EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. // #endregion #region Using Directives using System; using System.IO; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security.Permissions; using Microsoft.Win32.SafeHandles; #endregion namespace MaterialSkin.Gesture { /// /// Utility class to wrap an unmanaged DLL and be responsible for freeing it. /// /// This is a managed wrapper over the native LoadLibrary, GetProcAddress, and /// FreeLibrary calls. /// public sealed class UnmanagedLibrary : IDisposable { #region Safe Handles and Native imports // See http://msdn.microsoft.com/msdnmag/issues/05/10/Reliability/ for more about safe handles. [SecurityPermission( SecurityAction.LinkDemand, UnmanagedCode = true )] sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeLibraryHandle() : base( true ) { } protected override bool ReleaseHandle() { return NativeMethods.FreeLibrary( handle ); } } static class NativeMethods { const string KERNEL32 = "kernel32"; [DllImport( KERNEL32, CharSet = CharSet.Auto, BestFitMapping = false, SetLastError = true )] public static extern SafeLibraryHandle LoadLibrary( string fileName ); [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] [DllImport( KERNEL32, SetLastError = true )] [return: MarshalAs( UnmanagedType.Bool )] public static extern bool FreeLibrary( IntPtr hModule ); [DllImport( KERNEL32 )] public static extern IntPtr GetProcAddress( SafeLibraryHandle hModule, String procname ); } #endregion // Safe Handles and Native imports /// /// Constructor to load a dll and be responible for freeing it. /// /// full path name of dll to load /// if fileName can't be found /// Throws exceptions on failure. Most common failure would be file-not-found, or /// that the file is not a loadable image. public UnmanagedLibrary( string fileName ) { m_hLibrary = NativeMethods.LoadLibrary( fileName ); if ( m_hLibrary.IsInvalid ) { int hr = Marshal.GetHRForLastWin32Error(); Marshal.ThrowExceptionForHR( hr ); } } /// /// Dynamically lookup a function in the dll via kernel32!GetProcAddress. /// /// raw name of the function in the export table. /// null if function is not found. Else a delegate to the unmanaged function. /// /// 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. public TDelegate GetUnmanagedFunction( string functionName ) where TDelegate : class { IntPtr p = NativeMethods.GetProcAddress( m_hLibrary, functionName ); // Failure is a common case, especially for adaptive code. if ( p == IntPtr.Zero ) { return null; } Delegate 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; } #region IDisposable Members /// /// Call FreeLibrary on the unmanaged dll. All function pointers /// handed out from this class become invalid after this. /// /// 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). /// public void Dispose() { if ( !m_hLibrary.IsClosed ) { m_hLibrary.Close(); } } // Unmanaged resource. CLR will ensure SafeHandles get freed, without requiring a finalizer on this class. SafeLibraryHandle m_hLibrary; #endregion } }