P/Invokeを使用してC#とC++の間でメモリを共有する際に問題はありませんそれがどのように機能するか知っている私はMSDNのマーシャリングについて読むことをお勧めします。また、安全でないキーワードを使用してメモリを修正する方法についてもお読みください。ここで
は、あなたの変数は、単純な構造体として記述することができることを前提としていたサンプルである:Cで
++、次のようにあなたの関数を宣言します。
#pragma pack(1)
typedef struct VARIABLES
{
/*
Use simple variables, avoid pointers
If you need to use arrays use fixed size ones
*/
}variables_t;
#pragma pack()
extern "C"
{
__declspec(dllexport) int function1(void * variables)
{
// some work depending on random and on the "variables"
}
}
C#のは、このような何か:
を
variables_t variables = new variables_t();
//Initialize variables here
for(int i=0; i<many_times; i++)
{
int[] result = new int[number_of_parallel_tasks];
Parallel.For(0, number_of_parallel_tasks, delegate(int j)
{
result[j] = function1(ref variables)
});
// choose best result
// then update "variables"
}
:
そして、あなたのクラスで
他の形式のマーシャリングを使用して、独自のクラスを構築してアンマネージメモリに直接書き込むような、C++の構造の割り当てや解放などのより複雑なシナリオを使用できます。しかし、単純な構造体を使用して変数を保持できる場合は、上記の方法が最も簡単です。
EDIT:それは、単純なデータの例であれば、正しく、より複雑なデータ
を処理する方法のポインターはしたがって、上記のサンプルは私の意見ではC#とC++の間で「シェア」データへの正しい道です。基本型または固定サイズのプリミティブ型の配列を保持する構造体
これは、実際にC#を使用してメモリにアクセスする方法にはほとんど制限がないと言われています。詳細については、unsafeキーワード、fixedキーワード、およびGCHandle構造体を参照してください。そして、あなたが他の構造体の配列などを含む非常に複雑なデータ構造を持っているなら、あなたはもっと複雑な仕事をしています。
上記のケースでは、「変数」をC++に更新する方法のロジックを移動することをお勧めします。 はこのような何かを探すように機能++ Cに追加:
extern "C"
{
__declspec(dllexport) void updateVariables(int bestResult)
{
// update the variables
}
}
私はまだ私は、次の方式を提案するように、グローバル変数を使用しないようお勧めします。 C++で :C#ので
typedef struct MYVERYCOMPLEXDATA
{
/*
Some very complex data structure
*/
}variables_t;
extern "C"
{
__declspec(dllexport) variables_t * AllocVariables()
{
// Alloc the variables;
}
__declspec(dllexport) void ReleaseVariables(variables_t * variables)
{
// Free the variables;
}
__declspec(dllexport) int function1(variables_t const * variables)
{
// Do some work depending on variables;
}
__declspec(dllexport) void updateVariables(variables_t * variables, int bestResult)
{
// update the variables
}
};
:
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr AllocVariables();
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void ReleaseVariables(IntPtr variables);
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int function1(IntPtr variables);
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void updateVariables(IntPtr variables, int bestResult);
あなたはまだあなたには、次のような何かをする必要がありますC#であなたのロジックを維持したい場合: 返されたメモリを保持するクラスを作成します。 C++から独自のメモリアクセスロジックを作成してください。コピーセマンティクスを使用してC#にデータを公開します。あなたは構造がまだとして渡すことができます上記のサンプルでは、この
[DllImport("kernel32.dll")]
static extern void CopyMemory(IntPtr dst, IntPtr src, uint size);
[StructLayout((LayoutKind.Sequential, Pack=1)]
struct variable_t
{
public int int0;
public double double0;
public int subdata_length;
private IntPtr subdata;
public SubData[] subdata
{
get
{
SubData[] ret = new SubData[subdata_length];
GCHandle gcH = GCHandle.Alloc(ret, GCHandleType.Pinned);
CopyMemory(gcH.AddrOfPinnedObject(), subdata, (uint)Marshal.SizeOf(typeof(SubData))*subdata_length);
gcH.Free();
return ret;
}
set
{
if(value == null || value.Length == 0)
{
subdata_length = 0;
subdata = IntPtr.Zero;
}else
{
GCHandle gcH = GCHandle.Alloc(value, GCHandleType.Pinned);
subdata_length = value.Length;
if(subdata != IntPtr.Zero)
Marshal.FreeHGlobal(subdata);
subdata = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SubData))*subdata_length);
CopyMemory(subdata, gcH.AddrOfPinnedObject(),(uint)Marshal.SizeOf(typeof(SubData))*subdata_length);
gcH.Free();
}
}
}
};
[StructLayout((LayoutKind.Sequential, Pack=1)]
sturct SubData
{
public int subInt;
public double subDouble;
};
ような何かを
#pragma pack(1)
typedef struct SUBSTRUCT
{
int subInt;
double subDouble;
}subvar_t;
typedef struct COMPLEXDATA
{
int int0;
double double0;
int subdata_length;
subvar_t * subdata;
}variables_t;
#pragma pack()
C#で:私は何を意味することは、以下のように、 はあなたがC++で、このような構造を持っていると言うです最初のサンプルで。これはもちろん、構造体の配列内の構造体と構造体の配列を使用して複雑なデータを処理する方法の概要です。あなたが見ることができるように、あなたはメモリ破損から身を守るために多くのコピーが必要になります。また、メモリがC++経由で割り当てられている場合、FreeHGlobalを使用して解放すると非常に悪くなります。 メモリのコピーを避け、C#内のロジックを維持したい場合は、あなたが望むものに対してアクセサでネイティブメモリラッパーを書くことができます。例えば、N番目の配列メンバーのsubIntを直接設定または取得するメソッドがあります。あなたのコピーをあなたがアクセスしているものに正確に保ちます。
別のオプションは、難しいデータ処理を行うための特定のC++関数を記述し、ロジックに従ってC#から呼び出すことです。
最後に、CLIインターフェイスで常にC++を使用できます。しかし、私は自分がそうしなければならない場合にのみそれをする - 私は言葉が好きではないが、非常に複雑なデータについては、それを考慮する必要がある。
EDITは、私は完全を期すためDLLIMPORTに正しい呼び出し規約を追加しました。 DllImport属性で使用されるデフォルトの呼び出し規約はWinapi(Windowsでは__stdcallに変換される)ですが、C/C++のデフォルトの呼び出し規約(コンパイラオプションを変更しない限り)は__cdeclです。
私はあなたの質問をupvotedしかし、あなたは本当に良い答えを得るために "変数"の性質を指定する必要があります –
私はちょうど編集しました。ありがとうございました。 – 888