[C#] DLLImport를 사용하지 않고 DLL을 동적으로 호출
DLL을 동적으로 호출하려면 LoadLibrary + GetProcAddress에서 함수 포인터를 가져 와서 미리 정의 된 Delegate로 변환하고 호출하는 구식 방법이 있습니다. 동적으로 DLLImport 상당의 메소드를 작성해, 그것을 호출합니다.
이 방법의 장점으로는 사전에 Delegate를 정의할 필요가 없기 때문에, 파라미터 설정에 따라서 인수의 수, 형마저도 자유롭게 변경하는 것이 가능합니다. (그런 기회가 있을까라고 하면, 거의 없을지도 모르지만…)
코드
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.IO;
namespace ExecDllFuncTest
{
/// <summary>
/// PInvoke関数情報
/// </summary>
public class PInvokeProcInfo
{
/// <summary>
/// 関数名
/// </summary>
public string ProcName { get; set; }
/// <summary>
/// DLLファイル
/// </summary>
public string ModuleFile { get; set; }
/// <summary>
/// エントリポイント
/// </summary>
public string EntryPoint { get; set; }
/// <summary>
/// 戻り値の型(戻り値無しはSystem.Void)
/// </summary>
public Type ReturnType { get; set; } = typeof(void);
/// <summary>
/// 関数のパラメータの型
/// </summary>
public Type[] ParameterTypes { get; set; } = { };
/// <summary>
/// 呼び出し規約
/// </summary>
public CallingConvention CallingConvention { get; set; } = CallingConvention.StdCall;
/// <summary>
/// メソッドのキャラクターセット
/// </summary>
public CharSet CharSet { get; set; } = CharSet.Auto;
}
class Program
{
/// <summary>
/// PInvoke関数情報から、メソッドのメタデータを作成する。
/// </summary>
/// <param name="invInfo">PInvoke関数情報</param>
/// <returns>PInvoke関数メタデータ</returns>
public static MethodInfo CreateMethodInfo(PInvokeProcInfo invInfo)
{
string moduleName = Path.GetFileNameWithoutExtension(invInfo.ModuleFile).ToUpper();
AssemblyBuilder asmBld = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName("Asm" + moduleName), AssemblyBuilderAccess.Run);
ModuleBuilder modBld = asmBld.DefineDynamicModule(
"Mod" + moduleName);
TypeBuilder typBld = modBld.DefineType(
"Class" + moduleName,
TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder methodBuilder = typBld.DefinePInvokeMethod(
invInfo.ProcName,
invInfo.ModuleFile,
invInfo.EntryPoint,
MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PinvokeImpl | MethodAttributes.HideBySig,
CallingConventions.Standard,
invInfo.ReturnType,
invInfo.ParameterTypes.ToArray(),
invInfo.CallingConvention,
invInfo.CharSet);
methodBuilder.SetImplementationFlags(MethodImplAttributes.PreserveSig);
return typBld.CreateType().GetMethod(invInfo.ProcName);
}
private delegate int DlgMessageBox(IntPtr hWnd, string text, string caption, int buttonType);
static int Main(string[] args)
{
PInvokeProcInfo invInfo = new PInvokeProcInfo()
{
ProcName = "MessageBox",
EntryPoint = "MessageBoxW",
ModuleFile = "User32.dll",
ReturnType = typeof(Int32),
ParameterTypes = new Type[] { typeof(IntPtr), typeof(string), typeof(string), typeof(Int32) },
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode
};
//Invokeで実行
MethodInfo method = CreateMethodInfo(invInfo);
method.Invoke(null, new object[] { IntPtr.Zero, "Run Invoke", "test1", 0});
//Delegateで実行
DlgMessageBox messageBox = (DlgMessageBox)method.CreateDelegate(typeof(DlgMessageBox));
messageBox(IntPtr.Zero, "Run Delegate", "test2", 0);
return 0;
}
}
}
실행 결과
같은 DLL로부터 복수의 메소드를 정의하거나 같은 메소드를 몇번이나 사용하는 경우는, 작성이 끝난 TypeBuilder나 MethodInfo를 Dictionary로 캐쉬 해 두는 등이 좋을지도 모릅니다.
Reference
이 문제에 관하여([C#] DLLImport를 사용하지 않고 DLL을 동적으로 호출), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/radian-jp/items/2f3bdba833b27c79895c
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.IO;
namespace ExecDllFuncTest
{
/// <summary>
/// PInvoke関数情報
/// </summary>
public class PInvokeProcInfo
{
/// <summary>
/// 関数名
/// </summary>
public string ProcName { get; set; }
/// <summary>
/// DLLファイル
/// </summary>
public string ModuleFile { get; set; }
/// <summary>
/// エントリポイント
/// </summary>
public string EntryPoint { get; set; }
/// <summary>
/// 戻り値の型(戻り値無しはSystem.Void)
/// </summary>
public Type ReturnType { get; set; } = typeof(void);
/// <summary>
/// 関数のパラメータの型
/// </summary>
public Type[] ParameterTypes { get; set; } = { };
/// <summary>
/// 呼び出し規約
/// </summary>
public CallingConvention CallingConvention { get; set; } = CallingConvention.StdCall;
/// <summary>
/// メソッドのキャラクターセット
/// </summary>
public CharSet CharSet { get; set; } = CharSet.Auto;
}
class Program
{
/// <summary>
/// PInvoke関数情報から、メソッドのメタデータを作成する。
/// </summary>
/// <param name="invInfo">PInvoke関数情報</param>
/// <returns>PInvoke関数メタデータ</returns>
public static MethodInfo CreateMethodInfo(PInvokeProcInfo invInfo)
{
string moduleName = Path.GetFileNameWithoutExtension(invInfo.ModuleFile).ToUpper();
AssemblyBuilder asmBld = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName("Asm" + moduleName), AssemblyBuilderAccess.Run);
ModuleBuilder modBld = asmBld.DefineDynamicModule(
"Mod" + moduleName);
TypeBuilder typBld = modBld.DefineType(
"Class" + moduleName,
TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder methodBuilder = typBld.DefinePInvokeMethod(
invInfo.ProcName,
invInfo.ModuleFile,
invInfo.EntryPoint,
MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PinvokeImpl | MethodAttributes.HideBySig,
CallingConventions.Standard,
invInfo.ReturnType,
invInfo.ParameterTypes.ToArray(),
invInfo.CallingConvention,
invInfo.CharSet);
methodBuilder.SetImplementationFlags(MethodImplAttributes.PreserveSig);
return typBld.CreateType().GetMethod(invInfo.ProcName);
}
private delegate int DlgMessageBox(IntPtr hWnd, string text, string caption, int buttonType);
static int Main(string[] args)
{
PInvokeProcInfo invInfo = new PInvokeProcInfo()
{
ProcName = "MessageBox",
EntryPoint = "MessageBoxW",
ModuleFile = "User32.dll",
ReturnType = typeof(Int32),
ParameterTypes = new Type[] { typeof(IntPtr), typeof(string), typeof(string), typeof(Int32) },
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode
};
//Invokeで実行
MethodInfo method = CreateMethodInfo(invInfo);
method.Invoke(null, new object[] { IntPtr.Zero, "Run Invoke", "test1", 0});
//Delegateで実行
DlgMessageBox messageBox = (DlgMessageBox)method.CreateDelegate(typeof(DlgMessageBox));
messageBox(IntPtr.Zero, "Run Delegate", "test2", 0);
return 0;
}
}
}
같은 DLL로부터 복수의 메소드를 정의하거나 같은 메소드를 몇번이나 사용하는 경우는, 작성이 끝난 TypeBuilder나 MethodInfo를 Dictionary로 캐쉬 해 두는 등이 좋을지도 모릅니다.
Reference
이 문제에 관하여([C#] DLLImport를 사용하지 않고 DLL을 동적으로 호출), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/radian-jp/items/2f3bdba833b27c79895c텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)