#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 来声明和调用这个函数。 ``` cs 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。 ``` cs 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),以便正确地传递数据。 #### 示例: 将托管的结构体转换为非托管内存块,或者从非托管内存读取数据。 ``` cs 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(ptr); Console.WriteLine($"X: {newPoint.X}, Y: {newPoint.Y}"); // 释放非托管内存 Marshal.FreeHGlobal(ptr); } } ``` ### 4. 非托管资源的处理 使用 `GCHandle` 结构体,可以将托管对象转换为不可移动的句柄,允许非托管代码使用它。这样在垃圾回收期间不会移动对象的内存地址。 ``` cs 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` 可以捕获结构化异常处理中的错误。 ``` cs 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 应用中复用老的系统组件。