According to https://learn.microsoft.com/en-us/dotnet/framework/interop/marshalling-classes-structures-and-unions#unions-sample, value types and reference types (such as the strings) are not permitted to overlap. The provided sample uses two variants of the C# structures to simulate the C++ union. I think that you can try a similar approach:
extern "C"
{
enum pdll_test_id : __int32
{
dut_com_init = 0,
cont_pkt_tx = 1,
};
#pragma pack(push)
#pragma pack(4)
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;
};
#pragma pack(pop)
__declspec( dllexport ) int __cdecl 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;
}
}
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 );
The example also uses some additional declarations (maybe redundant) to make sure that C# communicates correctly with C++ in this experiment. You can also use the original unchanged C++ code and only adjust the C# declarations.
It is also possible to consider fixed arrays like fixed byte name[13] and pointers, however this requires activating the unsafe code option. Or maybe you can construct the data as a byte array and pass it to DLL.
Or it is possible to create an intermediate Class Library in C++/CLI that can interact with C# and C easier.
Therefore, if the DLL can be rebuilt, it is better to avoid unions.