Files
2025-07-14 12:03:59 +02:00

142 lines
5.8 KiB
C#

#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
{
/// <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
{
#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
/// <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="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 )
{
m_hLibrary = NativeMethods.LoadLibrary( fileName );
if ( m_hLibrary.IsInvalid )
{
int 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
{
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
/// <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 ( !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
}
}