出会いセンター C#を使ってDLL内の関数を動的に呼び出す方法

C#を使ってDLL内の関数を動的に呼び出す方法

ここでは、Reflectionによる動的なメソッド生成によってDLL内の関数を動的に実行する 方法について説明します。

手軽にやるならDefinePInvokeMethod()を使用する

方法については処々で話題になり色々な方法が考えれています。例えば、C++でアダプタを書いてそれをC#から利用する方法や、 GetProcAddressを使って取得した関数ポインタをIA-32アセンブリ言語を用いて直接実行する方法などです。 又、これはあまりメジャーではありませんが、.NET FrameworkではReflectionを通して動的にメソッドを定義できるので、 DefinePInvokeMethod()というメソッドを利用することができます。 この方法は非常に手軽に目的を達成できるので便利なのですが、 何故かライブラリの明示的な解放手段が提供されていません。 (若し存在する場合はご一報ください)これでは何かと困ることが出てくるかもしれません。 そこで、次のような方法を考えてみました。

Calliは関数ポインタを直接実行してくれる

一般的にC#ではCスタイルの関数ポインタ(デリゲートではない)を実行するこはできないとされています。 確かにC#という言語レヴェルでのサポートはありませんが、MSILではそれを可能にするオプコード(Calli)があります。 更にSystem.Reflection.Emit 名前空間にあるクラスを利用すれば、 動的にメタデータ及びMSILを生成することができるので、C#からでもCスタイルの関数ポインタを実行することは可能だということです。

次のようなヘルパークラスを作ってみました。

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


namespace DynamicallyPInvoke
{
    /// <summary>
    /// PInvokeMethodGen の概要の説明です。
    /// </summary>
    public class PInvokeMethodGen : IDisposable
    {
        #region Win32 API宣言
        
        [DllImport ("kernel32.dll", CharSet=CharSet.Auto)]
        private extern static IntPtr LoadLibrary(string lpFileName);

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

        [DllImport("kernel32.dll", CharSet=CharSet.Ansi)]
        private extern static IntPtr GetProcAddress(IntPtr hModule,    string lpProcName);
        
        #endregion


        #region privateフィールド
        
        private bool blnDisposed = false;
        private IntPtr ipLib = IntPtr.Zero;
        
        #endregion

        #region コンストラクタ

        public PInvokeMethodGen(string libName)
        {
            ipLib = LoadLibrary(libName);
        }

        #endregion

        /// <summary>
        /// 指定されたデリゲートスタイルで動的にAPI関数実行メソッドを生成する
        /// </summary>
        /// <param name="methodType">デリゲートのタイプを指定</param>
        /// <param name="methodName">DLL内に定義されている関数名</param>
        /// <returns>関数実行メソッドに関連付けられたデリゲート</returns>
        public System.Delegate MethodGen(Type methodType, string methodName)
        {
            AppDomain ad = Thread.GetDomain();
            AssemblyName asmName = new AssemblyName();
            asmName.Name = "MyAsm";

            AssemblyBuilder asmb = ad.DefineDynamicAssembly(asmName,
                                                            AssemblyBuilderAccess.Run);
            ModuleBuilder modb = asmb.DefineDynamicModule("MyMod");
            TypeBuilder typb = modb.DefineType("MyTyp", TypeAttributes.Public);

            MethodInfo mi = methodType.GetMethod("Invoke");
            ParameterInfo [] pi = mi.GetParameters();
            Type [] parametersType = new Type[pi.Length];
            for(int i=0; i<pi.Length; i++)
                parametersType[i] = pi[i].ParameterType;
            MethodBuilder metb = typb.DefineMethod("MyMethod",
                                                    MethodAttributes.Public,
                                                    mi.ReturnType, parametersType);
            
            ILGenerator ilg = metb.GetILGenerator();
            // 1番目以降つまり実際関数に送られる引数を積む
            for(int i=1; i<=parametersType.Length; i++)
                ilg.Emit(OpCodes.Ldarg, i);
            // 関数ポインタを積む
            ilg.Emit(OpCodes.Ldc_I4, (int)GetProcAddress(ipLib, methodName));
            ilg.EmitCalli(OpCodes.Calli,
                          CallingConvention.StdCall, mi.ReturnType, parametersType);
            ilg.Emit(OpCodes.Ret);

            
            Type t = typb.CreateType();
            object o = Activator.CreateInstance(t);
            return Delegate.CreateDelegate(methodType, o, "MyMethod");
        }
        #region IDisposable メンバ

        public void Dispose()
        {
            if(!blnDisposed)
            {
                FreeLibrary(ipLib);
                blnDisposed = true;
            }
        }

        #endregion
    }
}

デリゲートを通して実行しているので、多少オーバーヘッドはあるでしょうが、 特に深刻な問題とはならないでしょう。又、インターフェース経由で実行するという手も考えられます。では使い方です。

delegate bool BeepDelegate(uint dwFreq, uint dwDuration);
private void button_Click(object sender, System.EventArgs e)
{
    using(PInvokeMethodGen pimg = new PInvokeMethodGen("kernel32.dll"))
    {
        BeepDelegate Beep = (BeepDelegate)pimg.MethodGen(typeof(BeepDelegate), "Beep");
        Beep(262, 500);
    }
}

フォーム上のボタンが押下されたときにBeep関数が呼び出されるというシナリオです。

最後に問題点ですが、上記ヘルパークラスではマーシャリングに対して完全な対応をしていません。 既定のマーシャリングは行われるのですが、MarshalAsAttributeなどで指定したものは無視しています。 従って、blittableでない型は自前で整形して渡してやる必要があります。

無料 出会い系 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料出会い 無料 出会い系 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い掲示板 アクセスカウンター 無料 出会い 無料 出会い 無料 出会い デリヘル 広島 無料出会い 無料 出会い 無料 出会い 印鑑 アダルト動画 無料 出会い系 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料出会い 無料 出会い系 無料 出会い 無料 出会い 無料出会い 無料 出会い 無料 出会い オオクワガタ 無料 出会い