我有以下问题:
我有一个C#应用程序,它从非托管C ++ dll调用函数。在dll中有一个初始化函数,它在C#和C ++之间创建一个接口(基本上是一个值列表及其类型),它们将存储在一个struct中。
之后,有一个C#应用程序发送给dll的回调函数,dll每隔一段时间调用一次,它返回一个结构变量(或字节数组),就像它在接口中定义一样。 / p>
我的问题:你将如何传递和编组这个结构?是否可以传递结构本身,还是应该传递一个字节数组?
如果你传递一个字节数组,那么在返回C#app时你会如何编组?
我现在拥有的:
在C#app中:
对回调函数进行编组:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ProcessOutputDelegate(???); // not sure what should be here.
导入dll函数:
[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void Test(ProcessOutputDelegate ProcessOutput);
调用dll函数:
ProcessOutputDelegate process = new ProcessOutputDelegate(ProcessOutput);
new thread(delegate() { Test(process); } ).Start();
处理输出:
public void ProcessOutput(???)
{
// Assume we have a list of values that describes the struct/bytes array.
}
在C ++ dll中我有以下结构(这是一个例子,因为可以在不同的运行时调用不同的dll):
struct
{
int x;
double y;
double z;
} typedef Interface;
C#app调用的函数:
__declspec(dllexport) void Test(void (*ProcessOutput)(Interface* output))
{
int i;
Interface* output = (Interface*)malloc(sizeof(Interface));
for (i = 0; i < 100; i++)
{
sleep(100);
output->x = i;
output->y = i / 2;
output->z = i / 3;
ProcessOutput(output); // or generate a bytes array out of the struct
}
}
修改
C#应用程序是一个通用的GUI,它假设显示某些c ++ dll执行的繁重计算。在初始化过程中,dll告诉GUI应该呈现的变量(及其类型),并根据这些方向构建GUI(同样,计算和变量可能会改变,值可能是整数,浮点数,字符...)。之后,dll运行并在每几个时间步骤中调用回调函数来更新GUI。这应该适用于实现这个想法的任何dll:生成接口,然后根据此接口发送信息。
答案 0 :(得分:1)
我不知道,这是否是您问题的直接答案,但我通过以下方式解决了这个问题。
您在DLL中调用GetResource并获取IntPtr:
(...)
IntPtr res = Native.GetResource();
然后,您为此IntPtr实例化一个通用包装器:
ResourceWrapper rw = new ResourceWrapper(res);
如果要访问结构的特定字段,可以在包装器上调用适当的方法:
int field = rw.GetField();
Wrapper调用DLL中的函数:
(...)
int result = Native.GetField(res);
DLL“重新审视”电话:
__declspec(dllexport) int __stdcall GetField(MyStructure * s)
{
return s->GetField();
}
Struct获取数据(或抛出异常或设置错误标志或返回错误等)。
int MyStructure::GetField()
{
return this->field;
}
此解决方案需要进行以下更改:
您必须实施某种安全机制,以检查是否可以访问特定字段。最简单的是:
__declspec(dllexport) BOOL __stdcall GetField(MyStruct * s, int & result)
{
result = 0;
try
{
result = s->GetField();
}
catch(...)
{
return FALSE;
}
return TRUE;
}
和
protected class Native
{
[DllImport("MyDll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetField(IntPtr res, out int result);
}
和
// Wrapper
int GetField()
{
int result;
if !(Native.GetField(res, out result))
throw new InvalidOperationException("Resource does not support field!");
}