Architect's Log

I'm a Cloud Architect. I'm highly motivated to reduce toils with driving DevOps.

WININET.DLLの外部メソッド呼び出しで使用されるハンドルのラッパークラス

どういうこと?

WININET.DLLの外部メソッド呼び出しで使用されるハンドルのラッパークラスを定義します。

どうして?

IDisposableをインプリメントするので、using句を使えばハンドルの解放漏れがなくなります。

どうすれば?

ソースコードを記載します。

using System;
using System.Runtime.InteropServices;

namespace Shared.Net {

    /// <summary>
    /// WININET.DLLの外部メソッド呼び出しで使用されるハンドル
    /// (<see cref="System.IntPtr"/>)をラップします。
    /// </summary>
    public class InternetHandle : IDisposable {

        [DllImport("WININET", EntryPoint = "InternetCloseHandle", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool InternetCloseHandle(IntPtr hInternet);

        /// <summary>
        /// ハンドルが0かどうかを示します。
        /// </summary>
        /// <value>ハンドルが0で初期化されている場合はtrue。それ以外はfalse。</value>
        public bool IsZero {
            get {
                return this.pointer == IntPtr.Zero;
            }
        }

        /// <summary>
        /// このクラスの内部で保持しているハンドルです。
        /// </summary>
        /// <value>ハンドルが0の場合は無効なハンドル、それ以外の場合は有効なハンドルです。</value>
        public IntPtr Pointer {
            get {
                return this.pointer;
            }
        }
        IntPtr pointer = IntPtr.Zero;
    
        /// <summary>
        /// InternetHandleクラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="pointer">ハンドル</param>
        public InternetHandle(IntPtr pointer) {
            this.pointer = pointer;
        }

        #region 終了処理

        /// <summary>
        /// ガベージ コレクションによってクリアされる前に、アンマネージ リソースを解放し、
        /// その他のクリーンアップ操作を実行します。 
        /// </summary>
        ~InternetHandle() {
            Dispose();
        }

        /// <summary>
        /// 既にDisposeが呼ばれたかどうかを示します。
        /// </summary>
        /// <value>既にDisoseが呼ばれた場合はtrue。まだ呼ばれていない場合はfalse。</value>
        public bool Disposed {
            get { return this.disposed; }
        }
        private bool disposed = false;

        /// <summary>
        /// アンマネージ リソースの解放および終了処理を実行します。
        /// </summary>
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// アンマネージ リソースの解放および終了処理を実行します。
        /// </summary>
        /// <param name="disposing">
        /// マネージ リソースとアンマネージ リソースの両方を解放する場合は true。
        /// アンマネージ リソースだけを解放する場合は false。
        /// </param>
        private void Dispose(bool disposing) {
            if (disposed) return;

            // アンマネージリソースを解放する。
            if (this.IsZero) InternetHandle.InternetCloseHandle(this.pointer);
            this.pointer = IntPtr.Zero;

            disposed = true;
        }

        #endregion 終了処理

    }
}