System.Runtime.InteropServices 是 .NET 中的一个命名空间,提供了与 互操作(Interop) 相关的功能,主要用于在 .NET 应用程序中与非托管代码(如 C/C++、COM 组件、Win32 API 等)进行交互。通过这个命名空间,可以实现托管代码(.NET)与非托管代码之间的无缝集成。
主要功能
与非托管代码交互:
调用 Win32 API 或其他非托管库中的函数。
使用 COM 组件。
数据类型转换:
在托管代码和非托管代码之间传递数据时,处理数据类型的转换。
内存管理:
控制非托管内存的分配和释放。
自定义封送处理:
控制数据在托管和非托管代码之间的传递方式。
常见类和用法
1. DllImport 特性
用于声明外部非托管库中的函数,以便在 .NET 中调用。
示例:调用 Win32 API 的 MessageBox 函数
using System;
using System.Runtime.InteropServices;
class Program
{
// 声明 MessageBox 函数
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
static void Main()
{
// 调用 MessageBox
MessageBox(IntPtr.Zero, "Hello, World!", "Message", 0);
}
}
DllImport 参数:
"user32.dll":非托管库的名称。
CharSet:指定字符串的编码方式(如 CharSet.Unicode 或 CharSet.Ansi)。
CallingConvention:指定调用约定(如 CallingConvention.StdCall 或 CallingConvention.Cdecl)。
2. Marshal 类
提供了许多方法,用于在托管代码和非托管代码之间进行数据封送处理(Marshaling)。
常见方法:
Marshal.PtrToStringAnsi:将非托管 ANSI 字符串转换为托管字符串。
Marshal.StringToHGlobalAnsi:将托管字符串转换为非托管 ANSI 字符串。
Marshal.AllocHGlobal:分配非托管内存。
Marshal.FreeHGlobal:释放非托管内存。
Marshal.StructureToPtr:将托管结构体转换为非托管内存。
Marshal.PtrToStructure:将非托管内存转换为托管结构体。
示例:字符串封送处理
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
// 托管字符串
string managedString = "Hello, World!";
// 将托管字符串转换为非托管 ANSI 字符串
IntPtr unmanagedString = Marshal.StringToHGlobalAnsi(managedString);
// 将非托管字符串转换回托管字符串
string convertedString = Marshal.PtrToStringAnsi(unmanagedString);
Console.WriteLine(convertedString);
// 释放非托管内存
Marshal.FreeHGlobal(unmanagedString);
}
}
3. StructLayout 特性
用于控制托管结构体在内存中的布局,以便与非托管代码交互。
示例:定义与非托管代码兼容的结构体
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)] // 按顺序布局
struct Point
{
public int X;
public int Y;
}
class Program
{
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out Point lpPoint);
static void Main()
{
Point point;
if (GetCursorPos(out point))
{
Console.WriteLine($"Cursor Position: ({point.X}, {point.Y})");
}
}
}
LayoutKind:
Sequential:按顺序布局(默认)。
Explicit:显式指定每个字段的偏移量。
Auto:由运行时自动布局。
4. ComImport 和 Guid 特性
用于与 COM 组件交互。
示例:调用 COM 组件
using System;
using System.Runtime.InteropServices;
[ComImport]
[Guid("00021401-0000-0000-C000-000000000046")] // COM 组件的 GUID
class ShellLink
{
// COM 接口方法
}
class Program
{
static void Main()
{
// 创建 COM 对象
var shellLink = new ShellLink();
Console.WriteLine("COM object created.");
}
}
5. MarshalAs 特性
用于指定数据在托管和非托管代码之间的封送方式。
示例:指定字符串的封送方式
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string text, string caption, uint type);
static void Main()
{
MessageBox(IntPtr.Zero, "Hello, World!", "Message", 0);
}
}
UnmanagedType:
LPStr:ANSI 字符串。
LPWStr:Unicode 字符串。
BStr:COM 格式的字符串。
6. HandleRef 结构
用于安全地传递非托管句柄(如窗口句柄)以避免垃圾回收问题。
示例:使用 HandleRef
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(HandleRef hWnd, string text, string caption, uint type);
static void Main()
{
IntPtr hWnd = new IntPtr(0); // 假设的窗口句柄
HandleRef handleRef = new HandleRef(null, hWnd);
MessageBox(handleRef, "Hello, World!", "Message", 0);
}
}
总结
System.Runtime.InteropServices 提供了强大的功能,使 .NET 应用程序能够与非托管代码无缝集成。常见的用法包括:
使用 DllImport 调用 Win32 API。
使用 Marshal 类进行数据封送处理。
使用 StructLayout 定义与非托管代码兼容的结构体。
使用 ComImport 和 Guid 与 COM 组件交互。
通过合理使用这些功能,可以实现托管代码与非托管代码的高效互操作。