それでは始めましょう。
最初に行う必要があるのは、何か(この場合は[DllImport])を放棄すると、それを別のものに置き換えることです。 ここにあります。 P / Invoke(C#)を放棄し、Dll(C)からエクスポートすると、関数(C)のポインターとデリゲート(C#)のポインターを持つ構造体を宣言する必要があります。
次のコードを含むヘッダーファイルと実装ファイルがあるとします(どこから来たのか、どのように来たのかは関係ありません)。
NativeCode.hファイル
/* , */ typedef int (__stdcall * pFuncInterface)( __in int nStatus ); typedef struct _Interface { // pFuncInterface m_pfInterface; // DWORD m_dwData; } SInterface, *PSInterface; /* */ __declspec(dllexport) int CreateInterface( __inout PSInterface pInterface );
NativeCode.cppファイル
int __stdcall FuncInterface( __in int nStatus) { nStatus = 5; return 1; } /* */ int CreateInterface( __inout PSInterface pInterface) { // pInterface->m_pfInterface = FuncInterface; // - pInterface->m_dwData = 5; return pInterface->m_dwData; }
注1 :この例では、スタックの状態を復元するために__stdcallがリーダーによって既に追加されています。
注2 :構造体フィールド( SInterface )として宣言された関数ポインターの数は異なる場合があります。つまり、構造体には任意の数の関数ポインターを含めることができます。
例からわかるように、 intを取得してintを返す関数( FuncInterface )へのポインター( pFuncInterface )があり、2つのフィールドを持つ構造( SInterface )のうちの1つが関数へのポインターであり、フィールドによって構造を埋める関数( CreateInterface ) 。
私たちのタスクは、この構造との相互作用のためにC#でコードを書くことです。 最初に頭に浮かぶのは、C側のすべての関数をエクスポートし、C#側の[DllImport]を介して呼び出し、構造体にデータフィールドのみを残すことです(関数ポインターを除く)。 しかし、私はコードを変更したくありません(それがあなたのコードでない場合は、それを変更することはおそらく簡単で不可能です)。
この場合、次の手順に従ってください。
いつものように、Win32プロジェクト(コンソールアプリケーション)を作成し、それがDllでコンパイルされることを示し、C#プロジェクト-コンソールアプリケーションも作成します。 Dllでは、2つのソースファイルを追加し、エクスポートされたフィールドで構造を埋める機能を作成します(これは、P / Invokeの呼び出しを必要とする唯一の機能です)。 したがって、エクスポートされた関数CreateInterface()のみを持つDllがあります。
このメソッドのデメリットは、C#側で、関数ポインターがデリゲートに置き換えられた構造とまったく同じ構造を宣言する必要があることです(各関数でデリゲートを宣言する必要があります)。つまり、C#側では、本質的に* .hファイルコードが複製されます(この場合、 NativeCode.h )。 それでも、これを行うと、 CreateInterface()の [DllImport]が得られます。
using System; using System.Runtime.InteropServices; namespace SharpCode { /****************************************************/ // *.h public delegate int pInterface(int nStatus); [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SInterface { public pInterface m_pfpInterface; public UInt32 m_dwData; } /****************************************************/ class Program { private static SInterface stInterface = new SInterface(); [DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)] public static extern int CreateInterface( ref SInterface pInterface ); static void Main(string[] args) { CreateInterface(ref stInterface); // // unmanaged code P/Invoke int nRes = stInterface.m_pfpInterface(1); Console.WriteLine("Result = {0}", nRes); } } }
これで、 __ stdcallを2か所に追加するだけで、すべてが機能します。 DllからCreateInterface()を呼び出すと、 SInterface構造体には、C#側のデリゲートにマーシャリングされる関数(この場合は1つ)へのポインターが格納されます。 これで、取得したデリゲートを使用して、P / Invokeメカニズムをバイパスして、アンマネージコードから関数を直接呼び出すことができます。
予想どおりのプログラムの結果:

PS:このメソッドは、P / Invokeでアンマネージコードを呼び出す代わりにすぎません。