您好,登录后才能下订单哦!
在C#编程中,我们经常需要与非托管代码进行交互,例如调用Windows API、处理COM对象或与C/C++编写的库进行通信。为了实现这些功能,C#提供了Marshal
类,它是System.Runtime.InteropServices
命名空间中的一个重要工具类。Marshal
类提供了一系列方法,用于在托管代码和非托管代码之间进行数据转换、内存管理和类型封送(Marshaling)。
本文将详细介绍Marshal
类的基本概念,并通过实例代码分析其使用方法,帮助读者更好地理解和掌握这一强大的工具。
封送(Marshaling)是指在托管代码和非托管代码之间传递数据时,将数据从一种形式转换为另一种形式的过程。由于托管代码和非托管代码使用不同的内存管理方式和数据类型,因此在进行跨语言调用时,必须对数据进行适当的转换。
例如,托管代码中的string
类型与非托管代码中的char*
类型是不同的,因此在调用非托管函数时,需要将string
转换为char*
,这个过程就是封送。
Marshal
类提供了许多静态方法,用于处理托管代码和非托管代码之间的数据转换和内存管理。其主要功能包括:
Marshal
类提供了许多方法,以下是一些常用的方法:
Marshal.AllocHGlobal
:分配非托管内存。Marshal.FreeHGlobal
:释放非托管内存。Marshal.PtrToStringAnsi
:将非托管字符串指针转换为托管字符串。Marshal.StringToHGlobalAnsi
:将托管字符串转换为非托管字符串指针。Marshal.StructureToPtr
:将托管结构体转换为非托管内存块。Marshal.PtrToStructure
:将非托管内存块转换为托管结构体。Marshal.GetDelegateForFunctionPointer
:将非托管函数指针转换为托管委托。Marshal.GetFunctionPointerForDelegate
:将托管委托转换为非托管函数指针。在托管代码中,字符串是System.String
类型,而在非托管代码中,字符串通常是以char*
或wchar_t*
形式表示的。我们可以使用Marshal
类的方法在两者之间进行转换。
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
string managedString = "Hello, World!";
IntPtr unmanagedString = Marshal.StringToHGlobalAnsi(managedString);
try
{
// 调用非托管函数,传递unmanagedString
Console.WriteLine("Unmanaged string: " + Marshal.PtrToStringAnsi(unmanagedString));
}
finally
{
// 释放非托管内存
Marshal.FreeHGlobal(unmanagedString);
}
}
}
代码分析:
Marshal.StringToHGlobalAnsi
方法将托管字符串managedString
转换为非托管字符串指针unmanagedString
。Marshal.PtrToStringAnsi
方法将非托管字符串指针unmanagedString
转换回托管字符串。Marshal.FreeHGlobal
释放非托管内存,避免内存泄漏。using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
IntPtr unmanagedString = Marshal.StringToHGlobalAnsi("Hello, World!");
try
{
string managedString = Marshal.PtrToStringAnsi(unmanagedString);
Console.WriteLine("Managed string: " + managedString);
}
finally
{
Marshal.FreeHGlobal(unmanagedString);
}
}
}
代码分析:
Marshal.StringToHGlobalAnsi
方法将托管字符串转换为非托管字符串指针unmanagedString
。Marshal.PtrToStringAnsi
方法将非托管字符串指针unmanagedString
转换回托管字符串managedString
。Marshal.FreeHGlobal
释放非托管内存。在跨语言调用中,结构体的封送是一个常见的需求。Marshal
类提供了StructureToPtr
和PtrToStructure
方法,用于在托管结构体和非托管内存块之间进行转换。
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
struct Point
{
public int X;
public int Y;
}
class Program
{
static void Main()
{
Point point = new Point { X = 10, Y = 20 };
IntPtr unmanagedMemory = Marshal.AllocHGlobal(Marshal.SizeOf(point));
try
{
Marshal.StructureToPtr(point, unmanagedMemory, false);
// 调用非托管函数,传递unmanagedMemory
Point copiedPoint = (Point)Marshal.PtrToStructure(unmanagedMemory, typeof(Point));
Console.WriteLine($"Copied Point: X={copiedPoint.X}, Y={copiedPoint.Y}");
}
finally
{
Marshal.FreeHGlobal(unmanagedMemory);
}
}
}
代码分析:
Marshal.AllocHGlobal
方法分配非托管内存,大小为Point
结构体的大小。Marshal.StructureToPtr
方法将托管结构体point
转换为非托管内存块unmanagedMemory
。Marshal.PtrToStructure
方法将非托管内存块unmanagedMemory
转换回托管结构体copiedPoint
。Marshal.FreeHGlobal
释放非托管内存。using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
struct Point
{
public int X;
public int Y;
}
class Program
{
static void Main()
{
IntPtr unmanagedMemory = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Point)));
try
{
Point point = new Point { X = 10, Y = 20 };
Marshal.StructureToPtr(point, unmanagedMemory, false);
Point copiedPoint = (Point)Marshal.PtrToStructure(unmanagedMemory, typeof(Point));
Console.WriteLine($"Copied Point: X={copiedPoint.X}, Y={copiedPoint.Y}");
}
finally
{
Marshal.FreeHGlobal(unmanagedMemory);
}
}
}
代码分析:
Marshal.AllocHGlobal
方法分配非托管内存,大小为Point
结构体的大小。Marshal.StructureToPtr
方法将托管结构体point
转换为非托管内存块unmanagedMemory
。Marshal.PtrToStructure
方法将非托管内存块unmanagedMemory
转换回托管结构体copiedPoint
。Marshal.FreeHGlobal
释放非托管内存。在跨语言调用中,有时需要将托管委托转换为非托管函数指针,或者将非托管函数指针转换为托管委托。Marshal
类提供了GetFunctionPointerForDelegate
和GetDelegateForFunctionPointer
方法来实现这一功能。
using System;
using System.Runtime.InteropServices;
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int AddDelegate(int a, int b);
static int Add(int a, int b)
{
return a + b;
}
static void Main()
{
AddDelegate addDelegate = new AddDelegate(Add);
IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(addDelegate);
// 调用非托管函数,传递functionPointer
int result = ((AddDelegate)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(AddDelegate)))(3, 4);
Console.WriteLine("Result: " + result);
}
}
代码分析:
Marshal.GetFunctionPointerForDelegate
方法将托管委托addDelegate
转换为非托管函数指针functionPointer
。Marshal.GetDelegateForFunctionPointer
方法将非托管函数指针functionPointer
转换回托管委托。using System;
using System.Runtime.InteropServices;
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int AddDelegate(int a, int b);
static int Add(int a, int b)
{
return a + b;
}
static void Main()
{
AddDelegate addDelegate = new AddDelegate(Add);
IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(addDelegate);
AddDelegate copiedDelegate = (AddDelegate)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(AddDelegate));
int result = copiedDelegate(3, 4);
Console.WriteLine("Result: " + result);
}
}
代码分析:
Marshal.GetFunctionPointerForDelegate
方法将托管委托addDelegate
转换为非托管函数指针functionPointer
。Marshal.GetDelegateForFunctionPointer
方法将非托管函数指针functionPointer
转换回托管委托copiedDelegate
。Marshal
类是C#中用于处理托管代码与非托管代码之间交互的重要工具。通过Marshal
类,我们可以轻松地进行数据类型转换、内存管理、结构体封送和函数指针封送等操作。本文通过实例代码详细介绍了Marshal
类的基本概念和常用方法,希望能够帮助读者更好地理解和掌握这一强大的工具。
在实际开发中,Marshal
类的使用场景非常广泛,尤其是在与Windows API、COM对象或C/C++库进行交互时。掌握Marshal
类的使用方法,将大大提高我们在跨语言编程中的效率和灵活性。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。