当结构仅在运行时已知时,将结构从 c++ 传递到 c#

Posted

技术标签:

【中文标题】当结构仅在运行时已知时,将结构从 c++ 传递到 c#【英文标题】:Passing a struct from c++ to c# when the structure is only known during runtime 【发布时间】:2013-11-03 11:27:01 【问题描述】:

我有以下问题:

我有一个从非托管 C++ dll 调用函数的 C# 应用程序。 dll 中有一个初始化函数,它在 C# 和 C++ 之间创建一个接口(基本上是一个值及其类型的列表),该接口将存储在一个结构中。

之后,C# 应用程序向 dll 发送一个回调函数,该 dll 每隔一段时间就会调用一次,并返回接口中定义的结构变量(或字节数组)。

我的问题:你将如何传递和编组这个结构?是否可以传递结构本身,还是应该传递一个字节数组?

如果你传递一个字节数组,当返回到 C# 应用程序时你会如何编组它?

我现在拥有的:

在 C# 应用程序中:

封送回调函数:

[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# 应用调用的函数:

__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:生成一个接口,然后根据这个接口发送信息。

【问题讨论】:

我不明白 C++ 结构“可能会发生变化”的部分。在纯 C++ 中,结构是在编译时定义的。它们在运行时不会神奇地改变(当然是它们的内容,但不是它们的结构)。你能澄清一下吗? @Mat 没错,意思是每个运行时可能会调用不同的dll,我将添加一个说明。每个 dll 都会实现函数“Test”,但可能会构造不同的结构并执行不同的操作。 所以您正尝试对可能“返回”完全不同的东西的不同 C++ DLL 使用相同的 C# 接口?对不起,你的整个事情听起来很奇怪。如果没有具体示例来说明您将其应用于什么,那么(我认为)很难提供好的建议。如果它是完全随机的东西,可能与 XML 或 JSON 交互。 我很抱歉,但是通过这个设置你可以只使用一个字节指针或其他东西,但是你传递的结构的大小仍然未知,你需要传递sizeof(Interface) 值向量(类似于std::vector&lt;int&gt;)不是变量结构。如果 C# 代码不知道它会得到什么,它会如何处理它收到的任何内容? 【参考方案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!");

【讨论】:

以上是关于当结构仅在运行时已知时,将结构从 c++ 传递到 c#的主要内容,如果未能解决你的问题,请参考以下文章

如何将结构数组从 c++ 传递到 c#?

如何使用 pl sql 过程从结构仅在运行时知道的 oracle 表中动态获取数据?

Python:当列表仅在运行时确定时,如何将可迭代列表传递给 zip?

在 C# 中序列化这些值以在 C++ 中作为已知结构正确读取时遇到问题

将复杂的结构(带有结构的内部数组)从 C# 传递到 C++

如何将结构从 C++ 应用程序传递到 C++ Win32 DLL?