5.4 KiB
#unity/日常积累
InteropServices
是 .NET 框架中 System.Runtime.InteropServices
命名空间的一部分,用于处理 互操作性(Interop),也就是 .NET 程序与非托管代码(通常是原生的C、C++代码或者Win32 API)之间的交互。这允许你在托管环境中(如 C#)调用非托管代码或使用非托管资源。
InteropServices
提供了许多工具和功能来支持这种交互,包括 COM 互操作、平台调用(P/Invoke)、处理非托管资源等。以下是一些常见的使用场景和类/功能:
1. Platform Invocation (P/Invoke)
P/Invoke 是 InteropServices
中用于调用非托管代码(例如 C、C++ 编写的 DLL)的功能。通过 P/Invoke,可以从托管代码中调用非托管的函数。
示例:
假设你想调用 Windows API 函数 MessageBox
,可以使用 P/Invoke 来声明和调用这个函数。
using System;
using System.Runtime.InteropServices;
class Program
{
// 声明一个外部的非托管函数
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
static void Main()
{
// 调用非托管的 MessageBox 函数
MessageBox(IntPtr.Zero, "Hello, World!", "Message", 0);
}
}
2. COM Interop
COM(组件对象模型)是一种 Microsoft 技术,允许应用程序通过接口进行交互。InteropServices
提供了将托管代码与 COM 组件集成的功能,帮助 .NET 应用调用 COM 对象,或者将 .NET 对象暴露为 COM 对象。
示例:
通过 Marshal.GetActiveObject
调用一个已运行的 COM 对象,例如 Microsoft Excel。
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
try
{
// 获取一个正在运行的 Excel 实例
dynamic excel = Marshal.GetActiveObject("Excel.Application");
excel.Visible = true;
}
catch (COMException ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
}
3. Marshalling
InteropServices
提供的 Marshal
类帮助管理托管和非托管内存之间的数据转换。因为托管代码和非托管代码的内存布局和管理方式不同,某些情况下需要进行“封送处理”(Marshalling),以便正确地传递数据。
示例:
将托管的结构体转换为非托管内存块,或者从非托管内存读取数据。
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 ptr = Marshal.AllocHGlobal(Marshal.SizeOf(point));
Marshal.StructureToPtr(point, ptr, false);
// 从非托管内存读取回托管结构体
Point newPoint = Marshal.PtrToStructure<Point>(ptr);
Console.WriteLine($"X: {newPoint.X}, Y: {newPoint.Y}");
// 释放非托管内存
Marshal.FreeHGlobal(ptr);
}
}
4. 非托管资源的处理
使用 GCHandle
结构体,可以将托管对象转换为不可移动的句柄,允许非托管代码使用它。这样在垃圾回收期间不会移动对象的内存地址。
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
string managedString = "Hello, Interop!";
// 将托管对象固定在内存中
GCHandle handle = GCHandle.Alloc(managedString, GCHandleType.Pinned);
// 获取该对象的内存地址
IntPtr pointer = handle.AddrOfPinnedObject();
Console.WriteLine("Memory Address: " + pointer);
// 释放句柄,允许对象移动或被回收
handle.Free();
}
}
5. 安全处理非托管代码异常
InteropServices
提供一些机制来处理非托管代码中的异常。例如,SEHException
可以捕获结构化异常处理中的错误。
try
{
// 调用可能会抛出非托管异常的函数
}
catch (System.Runtime.InteropServices.SEHException sehEx)
{
Console.WriteLine("Caught unmanaged exception: " + sehEx.Message);
}
6. 调用 WinRT 组件
InteropServices
还可以帮助托管代码调用 WinRT(Windows Runtime)组件,使用类似于调用 COM 对象的方式。
常用类和功能:
Marshal
:提供了一些用于分配、释放、封送内存的静态方法,帮助托管代码与非托管代码互操作。DllImportAttribute
:用于 P/Invoke,声明非托管的函数。GCHandle
:用于在托管堆中固定对象的句柄,防止对象在调用非托管代码时被垃圾回收。SEHException
:处理非托管代码中的结构化异常。COMException
:处理与 COM 互操作相关的异常。CoCreateInstance
:用于从托管代码创建 COM 对象。
总结:
System.Runtime.InteropServices
是 .NET 的一个核心命名空间,它支持 .NET 应用与非托管代码(如 C++ DLL、Windows API、COM)之间的互操作。通过 P/Invoke、COM 互操作、封送处理等功能,开发者可以在托管代码中灵活使用现有的非托管代码库,或在新的 .NET 应用中复用老的系统组件。