アンマネージドからマネージドコードを呼び出す

この記事では、アンマネージCコードからマネージC#コード(.Net)を呼び出す方法について説明します。

仕事に出たら、彼らはプロジェクトを、あるいはプロジェクト自体ではなく、その一部だけを与えました。 プロジェクト自体は、Cで記述された機能(アンマネージコード)とC#で記述されたインターフェイスパーツ(マネージコード)の2つの部分で構成されていました。 私の仕事は、インターフェースを作成し、それを機能に関連付けることでした。



この記事の後半では、マネージコードをトップレベル、アンマネージ-ボトムと呼びます。



ご存知のように、P / Invoke(プラットフォーム呼び出し)メカニズムを使用して、C#の上部から下位レベルにアクセスします。 これを行うには、下位レベルをDllでラップし(つまり、下位レベルのすべての関数をエクスポートします)、DllImport属性を使用して上から呼び出します。 このメカニズムに不慣れな方-msdn.microsoft.com/en-us/library/aa288468(v = vs. 71).aspx



割り当ての過程で、私の前に問題が発生しました-下位レベルではコールバック関数で、関数の成功または失敗の完了を上位レベルに通知することでした。 この問題を解決するには、下位レベルから上位レベルを呼び出すか、上位レベル関数を呼び出す瞬間を見つけるための何らかのメカニズムを考え出す必要がありました(たとえば、イベントを使用)。 管理されていないコードから管理されたコードを呼び出すというトピックをインターネットで検索しても、成果はありませんでした。 それにもかかわらず、ほとんど血を流さずに、車輪を再発明しないようにすることが決定されました。



理解を簡単にするために、2つのプロジェクト(低レベルプロジェクト(CallC)と高レベルプロジェクト(SharpForCall))を含む新しいソリューションが作成されました。



そのため、空のC#プロジェクト(コンソールアプリケーション)があり、誰かがCプロジェクトを作成しました(最初はもちろんhファイルしか持っていませんでしたが、簡単にするためにプロジェクトをすぐに取ります)。 CのプロジェクトのタイプはDllです。これは、C#で受け取った実行可能ファイルの隣に当然あるはずです。 プロジェクトには、次の内容の* .cppファイルがあります。



/**    */ typedef VOID (__stdcall * pResultCallBack)( int nStatus ); __declspec(dllexport) int FuncC(pResultCallBack pfResult, PVOID pContext) { // //  -  // /*       (C#)   */ pfResult(1); return 1; }
      
      







もう一度、何をする必要があるのか​​を説明します。 ここでエクスポートされる関数(FuncC)はC#側にインポートされます。これは、ユーザーがボタンを押すと呼び出されます(メインタスクがインターフェイスを機能に接続することを忘れないでください)。 この関数(C#側にインポート)は、指定された* .cppファイル(上記参照)のFuncC関数を自然に呼び出します。これは、実行後、pResultCallBack関数を呼び出して実行結果をC#に報告します。 トップレベルでは、pResultCallBack関数(この場合はFuncCSharp、以下を参照)はFuncC関数の結果を分析し、渡された値に応じて特定のアクションを実行します(たとえば、失敗した呼び出しを示すステータスコードを返す場合、呼び出しを繰り返すことができます)。 d。)。 一般に、このアイデアは、あるマシン(ホスト)を別のマシンによって制御するために使用できます。



実装に取り​​かかりましょう。



最初に、[構成プロパティ]-> [全般]-> [出力ディレクトリ]でC-shプロジェクトの設定に移動し、C#プロジェクト実行可能ファイルを含むフォルダーへのパスを書き込みます。



画像



第二に、C#プロジェクトのプロジェクトの依存関係に移動し、Cプロジェクトの横にあるチェックボックスをオンにすることを忘れないでください。



画像



次に、Import.csクラスを作成します。このクラスでは、P / Invokeメカニズムを使用してインポートされた関数を記述します。



 using System; using System.Runtime.InteropServices;//    namespace SharpForCall { class Import { [DllImport("CallC.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int FuncC( [MarshalAs(UnmanagedType.FunctionPtr)] ResultCallBack pfResult, IntPtr pContext); } }
      
      







PVOIDをIntPtrに、pResultCallBack関数へのポインターをResultCallBackデリゲートに置き換えます。これは、Program.csファイルに次のように記述されています。



 using System; namespace SharpForCall { public delegate void ResultCallBack(Int32 nStatus); class Program { static void Main(string[] args) { ResultCallBack result = new ResultCallBack(FuncCSharp); IntPtr pContext = IntPtr.Zero; /*    Dll */ int nRes = Import.FuncC(result, pContext); } /**    . *   unmanaged . */ public static void FuncCSharp(Int32 nStatus) { if ( 1 == nStatus ) { //   } if ( 2 == nStatus ) { //   } //    return ; } } }
      
      







ここで、プログラムを開始して手順を実行することにより(アンマネージコードを入力するには、プロジェクトのプロパティでチェックボックスを設定する必要があります->デバッグ->アンマネージコードのデバッグを有効にする)、最初に上位レベルが下位レベルを呼び出し、(下位レベル)デリゲート、および下のデリゲート-FuncC関数の実行の最後に、同じ「デリゲート」を使用して上のデリゲート(FuncCSharp関数)を呼び出し、関数の実行結果(この場合は「1」)を渡します。 次に、関数は受信したステータスコードを分析し、制御を下位レベルに戻し、そこから制御が上位レベルに転送されます。 実行中にプログラムが次の内容の出口を発行した場合:



画像



、その後、C側に__stdcallコールバックの定義を追加します。



これが役に立たない場合は、クラスImportのC#側で、DllImport属性を呼び出すときにCallingConvention = CallingConvention.Cdeclを追加する必要があります。 これはすべて、スタックを元の状態に戻すために必要です。



これですべてが機能します。 多くの人が不可能だと考えていることを達成しました-アンマネージドからマネージドコードを呼び出しました。



PS:誰かが役に立つと思います。 コメントを待っています...



All Articles