C#動的呼び出しシステムDLL関数のクラス(DllImportを使用しない)

15092 ワード

using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Reflection.Emit;

public class DllCaller : IDisposable
{
[DllImport("Kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);

[DllImport("Kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

[DllImport("Kernel32.dll")]
static extern bool FreeLibrary(IntPtr hModule);

private IntPtr libPtr;
private MethodInfo method;
/// <param name="dllFile">DLL </param>
/// <param name="functionName"> , W A </param>
/// <param name="result"> , void, typeof(void)</param>
/// <param name="args"> </param>
/// <remarks>
/// : , , 。
/// : true,false bool; 0 int; (byte)0 byte, IntPtr.Zero
/// </remarks>
/// <example>
/// int MessageBox(
/// HWND hWnd,
/// LPCTSTR lpText,
/// LPCTSTR lpCaption,
/// UINT uType
/// );
/// DllCaller MessageBox = new DllCaller(
/// "user32.dll", "MessageBoxW", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, (uint)0
/// );
/// </example>
public DllCaller(string dllFile, string functionName, object result, params object[] args)
{
if (dllFile == null) throw new ArgumentNullException();
if (functionName == null) throw new ArgumentNullException();

this.libPtr = LoadLibrary(dllFile);
if (this.libPtr == IntPtr.Zero) throw new DllNotFoundException(dllFile);

IntPtr procPtr = GetProcAddress(this.libPtr, functionName);
if (procPtr == IntPtr.Zero) throw new EntryPointNotFoundException(functionName);

AssemblyName asmName = new AssemblyName();
asmName.Name = "DynamicAssembly";

AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("DynamicModule");

Type resultType = (result == typeof(void) ? typeof(void) : result.GetType());
Type[] argTypes = new Type[args.Length];
for (int i = 0; i < args.Length; i++)
argTypes[i] = args[i].GetType();

MethodBuilder funBuilder = modBuilder.DefineGlobalMethod(
functionName,
MethodAttributes.Public | MethodAttributes.Static,
resultType,
argTypes
);

ILGenerator ilGen = funBuilder.GetILGenerator();
for (int i = 0; i < args.Length; i++)
ilGen.Emit(OpCodes.Ldarg, i);

if (IntPtr.Size == 4)
ilGen.Emit(OpCodes.Ldc_I4, (int)procPtr);
else if (IntPtr.Size == 8)
ilGen.Emit(OpCodes.Ldc_I8, (long)procPtr);

ilGen.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, resultType, argTypes);
ilGen.Emit(OpCodes.Ret);
modBuilder.CreateGlobalFunctions();
this.method = modBuilder.GetMethod(functionName);
}

public object Call(params object[] args)
{
return this.method.Invoke(null, args);
}
public void Dispose()
{
if (this.method != null)
{
if (this.libPtr != IntPtr.Zero)
FreeLibrary(this.libPtr);
this.libPtr = IntPtr.Zero;
this.method = null;
GC.SuppressFinalize(this);
}
}
~DllCaller()
{
this.Dispose();
}
}

public class MarshalBuffer : IDisposable
{
public static MarshalBuffer FromString(string Text)
{
MarshalBuffer buffer = new MarshalBuffer();
buffer.ptr = Marshal.StringToCoTaskMemUni(Text);
return buffer;
}

public IntPtr ptr;
private MarshalBuffer() { }
public MarshalBuffer(int cb)
{
this.ptr = Marshal.AllocHGlobal(cb);
}
public void Dispose()
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptr);
this.ptr = IntPtr.Zero;
}
}
~MarshalBuffer()
{
this.Dispose();
GC.SuppressFinalize(this);
}
public override string ToString()
{
return Marshal.PtrToStringUni(this.ptr);
}
public int this[int Index]
{
get { return Marshal.ReadInt32(this.ptr, Index * 4); }
set { Marshal.WriteInt32(this.ptr, Index * 4, value); }
}
}

class Test
{
public static void DrawScreenTest()
{
DllCaller getdc = new DllCaller("user32.dll", "GetDC", IntPtr.Zero, IntPtr.Zero);
DllCaller textout = new DllCaller("gdi32.dll", "TextOutW", false, IntPtr.Zero, 0, 0, IntPtr.Zero, 0);
IntPtr dc = (IntPtr)getdc.Call(IntPtr.Zero);
MarshalBuffer text = MarshalBuffer.FromString("Hello World");
Random rand = new Random();
for (int i = 0; i < 100; i++)
textout.Call(dc, rand.Next(1024), rand.Next(768), text.ptr, "Hello World".Length);
}

public static void GetComputerNameTest()
{
MarshalBuffer text = new MarshalBuffer(100);
MarshalBuffer len = new MarshalBuffer(4);
len[0] = 50;
DllCaller getComName = new DllCaller("kernel32.dll", "GetComputerNameW", false, IntPtr.Zero, IntPtr.Zero);
getComName.Call(text.ptr, len.ptr);
Console.WriteLine(text.ToString());
Console.WriteLine(len[0]);
}

static void Main()
{
DrawScreenTest();
GetComputerNameTest();
}
}