How to pass Nested complex structure and Union from Managed C# dll to C++ dll

问题描述 投票:0回答:1

从 C++ dll 中导出方法,需要从 C# 中使用它。 C++ 方法具有复杂的嵌套结构和联合作为参数。我无法将字符串数据从 C# 发送到 C++。

C++以下,pdll_sensor_test结构体有name变量。无法将值从 C# 发送到 C++。它打印为空。

Source.cpp (C++)

#include "pch.h"

#include <stdio.h>

extern "C"
{
    enum pdll_test_id

    {
        dut_com_init = 0,
        cont_pkt_tx = 1,
    };

    struct pdll_sensor_test
    {
        pdll_test_id test_id;
        bool en;
        char name[13];
        int reg_addr;
    };

    union pdll_test_data
    {
        pdll_test_id id;
        pdll_sensor_test sensor;
    };

    struct pdll_device
    {
        bool is_active;
        pdll_test_data test;
    };

    __declspec(dllexport) int PdllDeviceMethod(pdll_device* pdlldevice)
    {
        printf("pdlldevice->is_active=%d\n", pdlldevice->is_active);
        printf("pdlldevice->test.sensor.test_id=%d\n", pdlldevice->test.sensor.test_id);
        printf("pdlldevice->test.sensor.en=%d\n", pdlldevice->test.sensor.en);
        printf("pdlldevice->test.sensor.name=%s\n", pdlldevice->test.sensor.name);
        printf("pdlldevice->test.sensor.reg_addr=%d\n", pdlldevice->test.sensor.reg_addr);
        return 0;
    }
}

Program.cs (C#)

using System;
using System.Runtime.InteropServices;

namespace CSharpApp
{
    public class Program
    {
        public enum pdll_test_id
        {
            dut_com_init = 0,
            cont_pkt_tx = 1,
        };

        [StructLayout(LayoutKind.Sequential)]
        public struct pdll_sensor_test
        {
            public pdll_test_id test_id;
            public bool en;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
            public string name;
            public int reg_addr;
        };

        //This is union in C++
        [StructLayout(LayoutKind.Explicit)]
        public struct pdll_test_data
        {
            [FieldOffset(0)]
            public pdll_test_id id;
            [FieldOffset(4)]
            public pdll_sensor_test sensor;
        };

        [StructLayout(LayoutKind.Sequential)]
        public struct pdll_device
        {
            public bool is_active;
            public pdll_test_data test;
        };

        [DllImport("CPPDynamicDLL.dll")]
        public static extern int PdllDeviceMethod(ref pdll_device pdlldevice);

        public static void Main(string[] args)
        {
            Console.WriteLine("This is C# program");
            pdll_device pdllDevice = new pdll_device();
            pdllDevice.is_active = true;
            pdllDevice.test.id = pdll_test_id.dut_com_init;
            pdllDevice.test.sensor.test_id = pdll_test_id.cont_pkt_tx;
            pdllDevice.test.sensor.en = true;
            pdllDevice.test.sensor.name = "wsa";
            pdllDevice.test.sensor.reg_addr = 1024;
            var result = PdllDeviceMethod(ref pdllDevice);
        }
    }
}

我尝试使用封送处理,但无法将变量名从 C# 发送到 C++。

c# c++ marshalling
1个回答
0
投票

根据https://learn.microsoft.com/en-us/dotnet/framework/interop/marshalling-classes-structures-and-unions#unions-sample,值类型和引用类型(例如字符串)不允许重叠。提供的示例使用 C# 结构的两个变体来模拟 C++ 联合。我认为你可以尝试类似的方法:

C#

public enum pdll_test_id : Int32
{
    dut_com_init = 0,
    cont_pkt_tx = 1,
};


[StructLayout( LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi )]
public struct pdll_sensor_test
{
    public pdll_test_id test_id;
    [MarshalAs( UnmanagedType.I1 )]
    public bool en;
    [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 13 )]
    public string name; 
    public int reg_addr;
};


[StructLayout( LayoutKind.Sequential, Pack = 4)]
public struct pdll_test_data_1
{
    public pdll_test_id id;
};


[StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct pdll_test_data_2
{
    public pdll_sensor_test sensor;
};


[StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct pdll_device_1
{
    [MarshalAs( UnmanagedType.I1 )]
    public bool is_active;
    public pdll_test_data_1 test;
};



[StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct pdll_device_2
{
    [MarshalAs( UnmanagedType.I1 )]
    public bool is_active;
    public pdll_test_data_2 test;
};


[DllImport( @"CPPDynamicDLL.dll", CallingConvention = CallingConvention.Cdecl )]
public static extern int PdllDeviceMethod( ref pdll_device_1 pdlldevice );

[DllImport( @"CPPDynamicDLL.dll", CallingConvention = CallingConvention.Cdecl )]
public static extern int PdllDeviceMethod( ref pdll_device_2 pdlldevice );


pdll_device_2 pdllDevice = new pdll_device_2( );
pdllDevice.is_active = true;
pdllDevice.test.sensor.test_id = pdll_test_id.cont_pkt_tx;
pdllDevice.test.sensor.en = true;
pdllDevice.test.sensor.name = "wsa";
pdllDevice.test.sensor.reg_addr = 1024;

var result = PdllDeviceMethod( ref pdllDevice );

该示例还使用了一些额外的声明(可能是多余的)来确保 C# 在本实验中与 C++ 正确通信。您也可以使用原始的未更改的 C++ 代码,只调整 C# 声明。

也可以考虑固定数组,如固定字节名称 [13] 和指针,但这需要激活不安全代码选项。或者您可以将数据构造为字节数组并将其传递给 DLL。

或者可以在 C++/CLI 中创建一个可以更轻松地与 C# 和 C 交互的中间类库。

因此,如果可以重建DLL,最好避免union。

© www.soinside.com 2019 - 2024. All rights reserved.