164 lines
5.4 KiB
Markdown
164 lines
5.4 KiB
Markdown
#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<Point>(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 应用中复用老的系统组件。 |