一旦您使用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#中做这样的事情:
[StructLayout(LayoutKind.Sequential, Pack=1)]
struct variables_t
{
/*
Place the exact same definition as in C++
remember that long long in c++ is long in c#
use MarshalAs for fixed size arrays
*/
};
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int function(ref variables_t variables);
而且在你的班级里:
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++中分配和释放结构,使用其他形式的编组来获取数据,如构建自己的类以直接读取和写入非托管内存。但是如果你可以使用一个简单的结构来保存你的变量,那么上面的方法是最简单的。
编辑:如何正确处理更复杂的数据
所以在上面的示例是在我看来,正确的方法指针“共享”数据C#和C++之间如果是简单的数据,例如。一个结构保存原始类型或固定大小的原始类型的数组。
这就是说,你可以使用C#访问内存的方式实际上有很小的限制。有关更多信息,请查看不安全关键字,固定关键字和GCHandle结构。如果你有一个非常复杂的数据结构,其中包含其他结构的数组等等,那么你的工作就更复杂了。
在上面的情况下,我建议移动如何将“变量”更新为C++的逻辑。 加入C++中的功能是这个样子:
extern "C"
{
__declspec(dllexport) void updateVariables(int bestResult)
{
// update the variables
}
}
我还是建议不要使用全局变量,所以我提出以下建议方案。 在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
}
};
在C#:
[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#公开数据。我的意思是如下, 说你有C++这样的结构:在C#
#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()
你做这样的事情
[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;
};
在上面的示例结构仍然可以作为传递在第一个样本中。这当然只是关于如何处理结构数组中结构和阵列结构的复杂数据。正如你所看到的,你需要大量的复制来防止内存损坏。另外,如果内存是通过C++分配的,如果使用FreeHGlobal释放内存将会非常糟糕。 如果你想避免复制内存,并仍然保持在C#中的逻辑,你可以编写一个本地内存包装访问器为你想要什么例如,你将有一个方法来直接设置或获取第N个数组成员的子类 - 这你会保持你的副本到你访问的方式。
另一种选择是编写特定的C++函数为您处理困难的数据处理,并根据您的逻辑从C#调用它们。
最后但并非最不重要的是,您始终可以使用C++与CLI接口。不过,我自己做这件事只有当我必须 - 我不喜欢这个术语,但对于非常复杂的数据,你当然必须考虑它。
编辑
我加入了正确的调用约定到的DllImport的完整性。请注意,DllImport属性使用的默认调用约定是Winapi(在Windows上转换为__stdcall),而C/C++中的默认调用约定(除非更改编译器选项)是__cdecl。
我把你的问题upvoted,但你应该真的指定“变量”的性质来得到一个很好的答案 –
我刚刚编辑。谢谢。 – 888