アンマネージド DLL を遅延バインディングで使用する


C# からアンマネージドな DLL を使用するには DllImportAttribute を用いた方法が多いのですが,動的に読み込みたい場合もあります。アンマネージド DLL を遅延バインディングするクラスは .NET Framework に用意されていないと思いますので,自前で用意してやる必要があります。

public class LateBoundUnmanagedDll : SafeHandle
{
	public override bool IsInvalid
	{
		get
		{
			return IsClosed || this.handle == IntPtr.Zero;
		}
	}

	public LateBoundUnmanagedDll(string dllName)
		: base(IntPtr.Zero, true)
	{
		IntPtr handle = LoadLibrary(dllName);
		SetHandle(handle);
	}

	protected override bool ReleaseHandle()
	{
		return FreeLibrary(this.handle);
	}

	public TDelegate GetFunction<TDelegate>(string entryPoint)
		where TDelegate : class
	{
		if (!typeof(TDelegate).IsSubclassOf(typeof(Delegate)))
		{
			throw new ArgumentException();
		}

		IntPtr function = GetProcAddress(this.handle, entryPoint);
		return Marshal.GetDelegateForFunctionPointer(function, typeof(TDelegate)) as TDelegate;
	}

	[DllImport("kernel32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool SetDllDirectory([MarshalAs(UnmanagedType.LPStr)] string lpPathName);

	[DllImport("kernel32.dll")]
	private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);

	[DllImport("kernel32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	private static extern bool FreeLibrary(IntPtr hModule);

	[DllImport("kernel32.dll")]
	private static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
}

GetFunction で関数ポインタをデリゲートに変換してやります。ジェネリックの制約に Delegate クラスが利用できないのが残念です。 if(!typeof(TDelegate).IsSubclassOf(typeof(Delegate))) の行がものすごく浮いている気がします。