P/Invoke各种总结(八、在C#中使用Union联合体)

Union联合体(共用体)

这里稍微简单介绍一下union这种类型。

有时候需要使几种不同类型的变量存放到同一段内存单元中,例如:可把一个整型变量、一个字符串变量放在同一个地址开始的内存单元中。以上两个变量在内存中占用的字节数不同,但都从同一个地址开始存放,也就是使用覆盖技术,几个变量互相覆盖。这种使用几个不同的变量共同占用同一段内存的结构,称为union。

1 union
2     {        
3         int a;
4         char b;
5     };

 

union的特点是:

1、同一个内存段可以用来存放几种不同类型的成员,但在同一时刻只能存放其中一种。

2、union起作用的成员是最后一次存放的变量,在存入一个新的成员后原有的成员就失去作用。

 

union和struct的区别

struct:所占内存长度是各成员占的内存之和,每个成员分别占用某个自己的内存单元

 

union:所占内存长度等于最长的成员的长度

 

在C#中使用union

在C#中与union交互,需要用到StructLayoutAttribute特性和FieldOffsetAttribute特性,关于这两个特性的介绍,可以参考https://www.cnblogs.com/zhaotianff/p/12510286.html

 

其实在知道union的原理后,在C#中进行映射也是非常简单。

 

如前面示例代码中的union,在C#中的表示形式如下:

1    [StructLayout(LayoutKind.Explicit)]
2     struct MyStruct
3     {
4         [FieldOffset(0)]int a;
5         [FieldOffset(0)]char b;
6    };

 

在上一篇文章中,介绍了  StructLayoutAttribute类的Pack字段,这里再介绍一下另外一个字段,StructLayoutAttribute.Size字段,Size字段用于指示类或结构的绝对大小

在使用union时,如果不是C#中的值类型,就需要指定大小。

 

测试程序:

最简单的情况:

C++

 1 union MYUNION
 2 {
 3     int b;
 4     double d;
 5 };
 6 
 7 extern "C" __declspec(dllexport) MYUNION GetMyUnion();
 8 
 9 extern "C" __declspec(dllexport) MYUNION GetMyUnion()
10 {
11     MYUNION  myunion;    
12     myunion.b = 10;
13     return myunion;
14 }

 

 C#

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Runtime.InteropServices;
 7 
 8 namespace UnionTest
 9 {
10     class Program
11     {
12         [DllImport("UnionLib.dll")]
13         public static extern MyUnion GetMyUnion();
14 
15         static void Main(string[] args)
16         {
17             
18             //只有值类型的情况
19             var myunion = GetMyUnion();
20             Console.WriteLine(myunion.b);        
21         }
22     }
23 }

运行结果:

 

带引用类型的情况:

需要注意的是:值类型和引用类型不允许重叠。所以在单独使用带引用类型的union时,需要分开处理。

这里定义了两个导出函数,TestUnion2函数可以传入MYUNION2,并进行输出。GetMyUnion2函数直接返回MYUNION2

C++

 1 union MYUNION2
 2 {
 3     int i;
 4     char str[128];
 5 };
 6 
 7 extern "C" __declspec(dllexport) void TestUnion2(MYUNION2 u, int type);
 8 
 9 extern "C" __declspec(dllexport) void TestUnion2(MYUNION2 u, int type)
10 {
11     if (type == 1)
12     {
13         std::cout << u.i << std::endl;
14     }
15     else
16     {
17         std::cout << u.str << std::endl;
18     }
19 }
20 
21 extern "C" __declspec(dllexport) MYUNION2 GetMyUnion2();
22 
23 extern "C" __declspec(dllexport) MYUNION2 GetMyUnion2()
24 {
25     MYUNION2  myunion2;
26     strcpy_s(myunion2.str, 11, "HelloWorld");
27     return myunion2;
28 }

 

C#

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Runtime.InteropServices;
 7 
 8 namespace UnionTest
 9 {
10     class Program
11     {
12         [DllImport("UnionLib.dll")]
13         public static extern IntPtr GetMyUnion2();
14 
15 
16         [DllImport("UnionLib.dll")]
17         public static extern void TestUnion2(MyUnion2_INT mm, int i);
18 
19         [DllImport("UnionLib.dll")]
20         public static extern void TestUnion2(MyUnion2_STR mm,int i);
21 
22         static void Main(string[] args)
23         {          
24             //使用了引用类型无法直接通过签名返回,需要进行转换
25             //如果这里是使用了MYUNION2中的i字段,可以直接使用MyUnion2_INT作为返回值
26             var myunion2Ptr = GetMyUnion2();
27             MyUnion2_STR unionStr = new MyUnion2_STR();
28             unionStr = (MyUnion2_STR)Marshal.PtrToStructure(myunion2Ptr, typeof(MyUnion2_STR));
29             Console.WriteLine(unionStr.str);
30 
31             //注意:值类型和引用类型不允许重叠
32             //在使用引用类型的情况下,无法直接返回union
33             MyUnion2_INT mu1 = new MyUnion2_INT();
34             mu1.i = 30;
35             TestUnion2(mu1, 1);
36 
37             MyUnion2_STR mu2 = new MyUnion2_STR();
38             mu2.str = "abc";
39             TestUnion2(mu2, 2);
40         }
41     }
42 
43 
44 
45     /// <summary>
46     /// 如果使用了union中的int字段,就使用这个结构体
47     /// </summary>
48     [StructLayout(LayoutKind.Explicit, Size = 128)]
49     public struct MyUnion2_INT
50     {
51         [FieldOffset(0)]
52         public int i;
53     }
54 
55     /// <summary>
56     /// 如果使用了union中的char[]字段,就使用这个结构体
57     /// </summary>
58     [StructLayout(LayoutKind.Sequential)]
59     public struct MyUnion2_STR
60     {
61         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
62         public string str;
63     }
64 
65 }

运行结果:

 

最看再看一下在struct中使用union的情况:

这段示例代码中使用了Windows Data Types(LPWSTR),在不同的平台中,指针的大小不一样,所以在C#中调用时,需要根据平台,定义对应的结构体。

C++

 1 struct MYSTRUCTUNION
 2 {
 3     UINT uType;
 4     union
 5     {
 6         LPWSTR pStr;
 7         char cStr[260];
 8     };
 9 };
10 
11 extern "C" __declspec(dllexport) MYSTRUCTUNION GetMyUnion3();
12 
13 extern "C" __declspec(dllexport) MYSTRUCTUNION GetMyUnion3()
14 {
15     MYSTRUCTUNION myunion;
16     myunion.uType = 0;
17     myunion.pStr = L"HelloWorld";
18     return myunion;
19 }

 

C#

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Runtime.InteropServices;
 7 
 8 namespace UnionTest
 9 {
10     class Program
11     {
12         [DllImport("UnionLib.dll")]
13         public static extern MyStructUnion GetMyUnion3();
14 
15         static void Main(string[] args)
16         {          
17             //结构体和联合体一起使用的情况
18             //引用类型部分使用指针再进行转换,直接用字符串将会封送失败
19             var myStructUnion = GetMyUnion3();
20             var str = Marshal.PtrToStringUni(myStructUnion.pStr);
21             Console.WriteLine(str);
22         }
23     }
24 
25 #if x86
26     /// <summary>
27     /// 32位
28     /// </summary>
29     [StructLayout(LayoutKind.Explicit, Size = 264)]
30     public struct MyStructUnion
31     {
32         [FieldOffset(0)]
33         public uint uType;
34 
35         [FieldOffset(4)]
36         public IntPtr pStr;
37 
38         [FieldOffset(4)]
39         public IntPtr cStr;
40     }
41 
42 #endif
43 
44 
45 #if x64
46     /// <summary>
47     /// 64位
48     /// </summary>
49     [StructLayout(LayoutKind.Explicit, Size = 272)]
50     public struct MyStructUnion
51     {
52         [FieldOffset(0)]
53         public uint uType;
54 
55         [FieldOffset(8)]
56         public IntPtr pStr;
57 
58         [FieldOffset(8)]
59         public IntPtr cStr;
60     }
61 #endif
62 
63 }

运行结果:

 

示例代码

posted @ 2020-11-17 11:47  zhaotianff  阅读(1083)  评论(0编辑  收藏  举报