bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] ネイティブハンドルを安全にラップするSafeHandle

[C#] ネイティブハンドルを安全にラップするSafeHandle

Mar 17, 2026 C# , .NET bucket-sort

C#ではGCや型安全によって、安全にメモリやリソースを扱えるよう設計されています。しかし、サードパーティ製ライブラリやデバイスとの連携では、ポインタやファイルハンドルといった低レベルなリソースを扱わざるを得ない場面もあります。こうした場面では、せっかくの安全な仕組みが崩れてしまうリスクがあります。その問題に対する解決策として推奨されているのが SafeHandle です。

■ SafeHandleとは何か

SafeHandle は、.NET においてアンマネージドリソース(特にOSのハンドル)を安全に扱うために用意された基底クラスです。

一言でまとめると、ネイティブハンドルを安全にラップするための仕組み です。

クラスの定義は次のようになっています。

abstract class SafeHandle : CriticalFinalizerObject, IDisposable

ここから分かる通り、SafeHandleは単なるラッパーではなく、次のような役割を持っています。

  • IDisposable を実装している
  • ファイナライザを持つ(しかも強化版)
  • ハンドルの寿命管理を担当する

つまり、SafeHandleは「ハンドルを持つオブジェクトの最終的な責任者」として設計されています。

なぜSafeHandleが必要なのか

SafeHandleの必要性を理解するには、まず従来の方法を見ておく必要があります。

昔は、ネイティブリソースは IntPtr で扱うのが一般的でした。

IntPtr handle = CreateFile(...);

// 使用

CloseHandle(handle);

一見シンプルですが、この方法にはいくつもの問題があります。

  • CloseHandle の呼び忘れ → リソースリーク
  • 例外でスキップ → リーク
  • 二重解放 → クラッシュ
  • 不正な値 → 未定義動作

これらはすべて、「人間が正しく管理すること」を前提としている という設計に起因します。

つまり、安全性がコードではなく運用に依存している 状態です。

SafeHandleの役割

SafeHandleは、この問題を解決するために導入された仕組みです。

SafeHandleを使うと、ハンドルは単なる数値(IntPtr)ではなく、「意味を持ったオブジェクト」として扱われます。

実装例

class MyHandle : SafeHandle
{
    public MyHandle() : base(IntPtr.Zero, true) {}

    public override bool IsInvalid => handle == IntPtr.Zero;

    protected override bool ReleaseHandle()
    {
        return CloseHandle(handle);
    }
}

この設計によって、次のような改善が得られます。

  • GCと連携して最終的に解放される
  • Disposeによる即時解放が可能
  • 二重解放が防止される
  • 例外が発生しても安全

「解放を忘れる前提」で安全性が担保されます。

使い方(P/Invokeとの関係)

SafeHandleは主にP/Invokeと組み合わせて使われます。

宣言

[DllImport("kernel32.dll")]
static extern MyHandle CreateFile(...);

利用側

using (var handle = CreateFile(...))
{
    // 使用
}

ここで重要なのは、呼び出し側が IntPtr を扱わない という点です。

これにより、

  • ハンドルの型安全性が向上する
  • 解放責任が明確になる
  • usingによる安全な利用が可能になる

SafeHandleの内部的な強み

SafeHandleが単なるラッパー以上の存在である理由は、その内部実装にあります。

CriticalFinalizerObject

SafeHandleは CriticalFinalizerObject を継承しています。

これは通常のファイナライザよりも強い保証を持ちます。

  • CLRが異常状態でも実行される
  • 解放処理がより確実に行われる

⇒ 「最後の砦」としての役割

ReleaseHandle()

protected override bool ReleaseHandle()
  • 実際の解放処理を書く場所
  • DisposeとGCの両方から呼ばれる

⇒ 解放ロジックを一箇所に集約できる

IntPtrとの比較

SafeHandleの価値は、IntPtrとの比較でより明確になります。

項目 IntPtr SafeHandle
型安全 ❌ ✔
自動解放 ❌ ✔
例外安全 ❌ ✔
二重解放防止 ❌ ✔

結論:IntPtrは低レベルすぎるため、直接扱うべきではない

.NETの設計方針

Microsoftの設計ガイドラインでも、SafeHandleの利用が推奨されています。

方針

新規コードではSafeHandleを使う

実際の.NET内部でも、

  • FileStream
  • SafeFileHandle

などでSafeHandleが利用されています。

つまり、SafeHandleは内部実装レベルでも標準的な仕組み です。

参考ページ

Implement a Dispose method - .NET | Microsoft Learn
https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose

Dispose Pattern - Framework Design Guidelines | Microsoft Learn
https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern

Native interoperability best practices - .NET | Microsoft Learn
https://learn.microsoft.com/en-us/dotnet/standard/native-interop/best-practices

IDisposableとの関係

SafeHandleは IDisposable を実装しています。

  • usingで扱える
  • Disposeで即時解放できる
  • GCでも最終的に解放される

つまり、IDisposableの仕組みの上に乗る形で、安全性をさらに強化している わけです。

位置づけの理解

SafeHandleは、リソース管理の中で「最下層」に位置します。

構造

アプリコード
  ↓
Stream / SqlConnection
  ↓
SafeHandle
  ↓
OSハンドル

⇒ すべての安全性の基盤となるレイヤ

上位のクラス(Streamなど)が安全に見えるのは、内部でSafeHandleが守っているから です。

まとめ

SafeHandleとは

  • アンマネージドハンドルの安全なラッパー
  • IDisposable + 強化ファイナライザを持つ

解決している問題

  • リソースリーク防止
  • 例外安全
  • 二重解放防止

実務ルール

  • IntPtrを直接扱わない
  • P/InvokeではSafeHandleを使う
  • usingで管理する

最も重要な理解

「ハンドルを値ではなくオブジェクトとして扱う」

この発想の転換によって、ネイティブリソースの管理が「人間の注意」から「仕組み」に移されています。

C# .NET
← [C#] IDisposeの役割と使いどころ [C#] スレッドとは何か →

Related Posts

  • [C#] delegate と event の仕組みを整理する Apr 1, 2026
  • Pub/Sub パターンとは何か Mar 31, 2026
  • [C#] Interlockedの使い方 Mar 27, 2026
  • [C#] ImmutableHashSet<T>の使い方 Mar 26, 2026

Table of Contents

  • ■ SafeHandleとは何か
  • なぜSafeHandleが必要なのか
  • SafeHandleの役割
    • 実装例
  • 使い方(P/Invokeとの関係)
    • 宣言
    • 利用側
  • SafeHandleの内部的な強み
    • CriticalFinalizerObject
    • ReleaseHandle()
  • IntPtrとの比較
  • .NETの設計方針
    • 方針
    • 参考ページ
  • IDisposableとの関係
  • 位置づけの理解
    • 構造
  • まとめ

Recent Posts

  • Laravel の Event / Listener で Pub/Sub を実装する Apr 2, 2026
  • [C#] delegate と event の仕組みを整理する Apr 1, 2026
  • Pub/Sub パターンとは何か Mar 31, 2026
  • PHP/Laravel で値の状態を判定するヘルパ関数まとめ Mar 30, 2026
  • Filament の dehydrated メソッドとは何か Mar 29, 2026

Categories

  • AWS27
  • C#22
  • .NET20
  • Laravel16
  • Linux12
  • Apache8
  • MySQL8
  • PHP8
  • DynamoDB6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • セキュリティ3
  • .NET Framework1
  • Aurora1
  • Filament1
  • Git1
  • SQS1

Tags

  • AWS
  • C#
  • .NET
  • Laravel
  • PHP
  • MySQL
  • セキュリティ
  • Linux
  • Apache
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • DoS
  • Nginx
  • Windows
  • WordPress
  • パフォーマンス
  • 監視
  • Amazon Linux 2023
  • CMS
  • Docker
  • Ipset
  • Iptables
  • OPCache
  • Webサーバー
  • 認可
  • Aurora
  • Blade
Powered by Hugo & Explore Theme.