C#からネイティブコードを呼び出す。 マーシャリング構造



開発者は、マネージコードからネイティブメソッドを呼び出すタスクを開始し始めました。 ほとんどの場合、呼び出しは非常に簡単ですが、動的構造の配列を含む構造の動的配列を持つ構造を含むメソッドなど、不快なケースが発生する場合があります。





タスクについて少し



C ++で記述されたdllがあり、ソースコードには次の宣言が含まれています。

#pragma pack (push, 4) struct vpsMSR { unsigned __int64 data; unsigned int address; }; #pragma pack (pop) struct vpsConfCounter { int address; int number; vpsMSR *config; unsigned int configCount; }; struct vpsConfig { int processorsCount; vpsConfCounter *counters; unsigned int countersCount; bool printToScreen; std::wstring activityName; }; extern "C" VPS::ErrorCode InitConfig(vpsConfig conf); extern "C" VPS::ErrorCode ClearConfig(vpsConfig conf);
      
      







タスクは、マネージコードからメソッド呼び出しを実装することです。



可能なアプローチ



問題の解決に失敗し、動作しませんでした。 ソリューションを検索すると、1つのHowToが見つかりました。 構造の動的配列をマーシャリングする可能性について簡単に説明しますが、それを使用すると、各ポインターに多くの「サービス」コードがあります。 この記事で説明されているマーシャリングパスを選択する場合は、fixedキーワードを使用して配列を宣言する必要があります。そうしないと、問題が発生する可能性があります。

問題の本質は、GCが第2レベルのネストを無視し、構造を移動できることです。このアクションの後、ポインターには誤った値が含まれます。



決定



上記のメソッドは非常に正確な作業を必要とし、読みにくくする余分なコードを大量に生成するためです。 一時的な構造を使用することが決定されました。

 namespace ManagedTemp { #pragma pack (push, 4) struct vpsMSR { unsigned __int64 data; unsigned int address; }; #pragma pack (pop) struct vpsConfCounter { int address; int number; vpsMSR config[10]; unsigned int configCount; }; struct vpsConfig { int processorsCount; vpsConfCounter counters[10]; unsigned int countersCount; bool printToScreen; wchar_t* activityName; }; }
      
      





これらの構造では、一定の長さの配列が使用されます。 制限-サブジェクトエリアによって指示され、実際には達成されません。

D dllの2つのメソッドが追加されました。これらのメソッドは、配列の長さが一定の構造を取り、元のメソッドに転送します。

 extern "C" VPS::ErrorCode InitConfig2(ManagedTemp::vpsConfig* conf); extern "C" VPS::ErrorCode ClearConfig2(ManagedTemp::vpsConfig* conf);
      
      







マーシャリング



上記の構造のマーシャリングに進みます。

 [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct MSR { [MarshalAs(UnmanagedType.U8)] public System.UInt64 data; public int adress; } [StructLayout(LayoutKind.Sequential)] public struct ConfCounter { public int adress; public int number; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public MSR[] config; [MarshalAs(UnmanagedType.U4)] public uint configCount; } [StructLayout(LayoutKind.Sequential), Serializable] public struct Config { public int processorCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public ConfCounter[] counters; [MarshalAs(UnmanagedType.U4)] public uint countersCount; [MarshalAs(UnmanagedType.Bool)] public bool printToScreen; [MarshalAs(UnmanagedType.LPWStr)] public string activityName; }
      
      







マーシャリング方法:

  [DllImport(@"..\..\IConfigure.dll", EntryPoint = "InitConfig2")] public static extern int InitConfig(ref Config conf); [DllImport(@"..\..\IConfigure.dll", EntryPoint = "ClearConfig2")] public static extern int ClearConfig(ref Config conf);
      
      





C ++ dllではstdcallがデフォルトで設定されているため、CallingConvention属性を介して呼び出しのタイプを明示的に指定する必要はありません。



発生した問題とその解決方法



マーシャリングを実装すると、StackUnbalancedエラー/警告が表示されます。 どうする



私はこのエラーを最も頻繁に見なければなりませんでした-インポートされた関数と元の関数の署名が一致しないことを意味します。

理由:

1.不正なタイプのマシュアリング。

2.異なる呼び出し規約。

診断方法:最初に、呼び出し規約の適合性を確認してから、フィールドごとに構造を転送し、エラーが発生したフィールドを確認します。



intを__int64にキャストできない



PInvokeはintをint64にキャストしません。 したがって、System.Int64型を使用する必要がありました。



この資料は、UNNのITLab研究所での実務経験に基づいています。 このプロセスでは、 HowTo記事とmsdnでのマーシャリングに関する情報を使用しました。



All Articles