目录

C# 教程:Handle (句柄)

在 C# 和 .NET 开发中,Handle (句柄) 是一个非常重要的概念,尤其是在处理非托管资源(如文件、窗口、网络连接、GDI 对象)时。

1. 什么是句柄?

句柄(Handle)本质上是一个整数值(通常是 32 位或 64 位),操作系统(OS)用它来标识内存中的对象。

2. 常见的句柄类型

在 Windows 编程中,常见的句柄包括: * HWND: 窗口句柄 (Window Handle) * HFILE: 文件句柄 (File Handle) * HDC: 设备上下文句柄 (Device Context Handle) * HICON: 图标句柄

3. 在 C# 中使用句柄

在 C# 中,我们主要通过 `System.IntPtr` 来存储句柄,并使用 `System.Runtime.InteropServices` 命名空间来操作它们。

3.1 获取窗口句柄

最常见的场景是获取当前进程或外部进程的主窗口句柄。

using System;
using System.Diagnostics;
 
class Program
{
    static void Main()
    {
        // 获取当前进程
        Process currentProcess = Process.GetCurrentProcess();
 
        // 获取主窗口句柄 (IntPtr 类型)
        IntPtr hWnd = currentProcess.MainWindowHandle;
 
        Console.WriteLine($"当前窗口的句柄地址: {hWnd}");
        Console.WriteLine($"句柄的整数值: {hWnd.ToInt64()}");
    }
}

3.2 SafeHandle (安全句柄)

直接使用 `IntPtr` 存在风险,因为如果忘记释放句柄,会导致内存泄漏。从 .NET Framework 2.0 开始,微软引入了 `SafeHandle` 类。

SafeHandle 的优势: * 自动释放:实现了 `IDisposable` 和终结器(Finalizer),确保资源被释放。 * 防止回收:在 P/Invoke 调用期间防止对象被垃圾回收。

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.IO;
 
public class FileReader : IDisposable
{
    // 引入 Windows API CreateFile
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
        uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
        uint dwFlagsAndAttributes, IntPtr hTemplateFile);
 
    private SafeFileHandle _handle;
 
    public FileReader(string path)
    {
        // 使用 SafeFileHandle 接收非托管句柄
        _handle = CreateFile(path, 0x80000000, 1, IntPtr.Zero, 3, 0x80, IntPtr.Zero);
 
        if (_handle.IsInvalid)
        {
            throw new Exception("无法打开文件,句柄无效。");
        }
        Console.WriteLine("文件句柄获取成功,且是安全的。");
    }
 
    public void Dispose()
    {
        // SafeHandle 会自动处理释放逻辑
        _handle?.Dispose();
    }
}

4. 句柄泄漏 (Handle Leak)

句柄泄漏是指程序请求了句柄但未能将其归还给操作系统。

  1. 总是实现 `IDisposable` 模式。
  2. 使用 `using` 语句块。
  3. 优先使用 `SafeHandle` 而不是裸露的 `IntPtr`。

5. 总结

概念 描述 C# 对应类型
IntPtr 通用句柄表示,就是一个整数指针 `System.IntPtr`
SafeHandle 封装了 IntPtr 的安全包装器,支持自动释放 `Microsoft.Win32.SafeHandles.SafeHandle`
GCHandle 用于在非托管代码中固定托管对象,防止被 GC 移动 `System.Runtime.InteropServices.GCHandle`

在进行 Win32 API 调用或与 C++ 库交互时,理解句柄至关重要。始终牢记:谁申请,谁释放(除非使用了 SafeHandle)。