共享C#和C 之间的变量
Sharing variables between C# and C++
我正在编写C#中的软件,该软件需要多次调用,并且在许多线程中,在C 未管理的DLL中,函数是一个函数。
我有一个这样的C 文件:
// "variables" which consist in some simple variables (int, double)
// and in some complex variables (structs containing arrays of structs)
extern "C"
{
__declspec(dllexport) int function1()
{
// some work depending on random and on the "variables"
}
}
和像这样的C#类
public class class1
{
// "variables" <--- the "same" as the C++ file's ones
// Dll import <--- ok
public void method1()
{
int [] result;
for(int i=0; i<many_times; i++)
{
result = new int[number_of_parallel_tasks];
Parallel.For(0, number_of_parallel_tasks, delegate(int j)
{
// I would like to do result[j] = function1()
});
// choose best result
// then update "variables"
}
}
}
我写了"我想做...",因为每回合的C 功能也需要更新的"变量"。
我的问题是:
是否可以在C 和C#之间共享内存,以避免每次传递参考?这只是浪费时间吗?
我阅读了有关内存映射文件的信息。他们可以帮我吗?但是,您知道更合适的解决方案吗?
非常感谢您。
一旦您知道它的工作原理,就可以使用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#。我的意思是如下,假设您有这样的结构:
#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#中,您做这样的事情
[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#中维护逻辑,则可以用访问器编写一个本机内存包装器,例如您想要的任何内容,例如,您将有一种直接设置或获得Nth Array成员的subint的方法 - 您将保留副本以准确访问。
另一个选择是编写特定的C 功能,以为您完成困难的数据处理,并根据您的逻辑从C#拨打它们。
,最后但并非最不重要的一点是,您可以随时将C 与CLI接口一起使用。但是我自己只有在必须的情况下才这样做 - 我不喜欢术语,但是对于非常复杂的数据,您当然必须考虑它。
编辑
我将正确的呼叫约定添加到了dllimport,以完成完整性。请注意,dllimport属性使用的默认调用约定是winapi(在Windows上转换为__STDCALL),而C/C 中的默认调用召集约定(除非您更改编译器选项)为__cdecl。
您能做的最好的事情是定义您的C 代码(我认为这将是不受管理的)。然后在C /CLI中写一个包装器。包装器将为您提供C#的接口,并且是您可以照顾行进的地方(将数据从Unmanagerd移至托管区域)
避免不得不传递C#和C 代码所需的变量/数据的一种方法是从本机DLL导出两个函数。除了完成工作的功能外,还提供了另一个函数,该功能允许引用并将其存储到文件范围静态指针中,该指针在同一.CPP文件中定义为两个函数,以便双方都可以访问。
正如您提到的
- 我的 c++ 程序似乎没有发现字符串和我拥有但输入使用 getline 的变量之间的比较
- C++ 编译时在两个变量之间交替
- 创建变量之间的运算符排列
- 计算两个uint8_t变量之间差值的最快方法是什么?
- lambda 表达式中引用捕获的 constexpr 变量和非显式捕获的 constexpr 变量之间的区别
- 计算机如何分配两个变量,我们如何计算两个变量之间的距离?
- 类和变量之间的"ampersand operator"是什么意思?
- 函数参数和临时变量之间复制构造函数的奇怪行为
- 文字符号和字符串变量之间的串联然后返回常量字符*
- 变量之间的关系
- 单例变量和全局变量之间的差异
- 为什么堆栈中的函数局部变量之间存在内存空间
- 在C 中的三个布尔变量之间切换
- 物理寄存器和英特尔SIMD变量之间的关系
- C++无法在单独函数中的变量之间建立通信
- c和c++上下文中静态、自动、全局和局部变量之间的差异
- 如何在我存储的两个变量之间生成随机数
- 在变量之间移动队列而不复制元素
- 什么是介于2 int变量之间的mean管道运算符
- C 和 C++ 的静态变量之间的差异