SafeHandle 클래스

정의

운영 체제 핸들의 래퍼 클래스를 나타냅니다.Represents a wrapper class for operating system handles. 이 클래스는 상속되어야 합니다.This class must be inherited.

public ref class SafeHandle abstract : System::Runtime::ConstrainedExecution::CriticalFinalizerObject, IDisposable
[System.Security.SecurityCritical]
public abstract class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
type SafeHandle = class
    inherit CriticalFinalizerObject
    interface IDisposable
Public MustInherit Class SafeHandle
Inherits CriticalFinalizerObject
Implements IDisposable
상속
파생
특성
구현

예제

다음 코드 예제에서는 safehandle에서 파생 되는 운영 체제 파일 핸들에 대 한 사용자 지정 SafeHandleZeroOrMinusOneIsInvalid합니다.The following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. 파일에서 바이트를 읽고 해당 16 진수 값을 표시 합니다.It reads bytes from a file and displays their hexadecimal values. 테스트 도구도 오류가 포함 된 스레드를 중단 되도록 하지만 핸들 값이 해제 됩니다.It also contains a fault testing harness that causes the thread to abort, but the handle value is freed. 사용 하는 경우는 IntPtr 핸들을 나타내기 위해 핸들 비동기 스레드 중단으로 인해 손실 가끔 됩니다.When using an IntPtr to represent handles, the handle is occasionally leaked due to the asynchronous thread abort.

컴파일된 애플리케이션과 동일한 폴더에 있는 텍스트 파일을 해야 합니다.You will need a text file in the same folder as the compiled application. "HexViewer" 애플리케이션에 이름을 지정할 가정 명령줄 사용법은:Assuming that you name the application "HexViewer", the command line usage is:

HexViewer <filename> -Fault

필요에 따라 지정 -Fault 의도적으로 특정 창에서 스레드를 중단 하 여 핸들이 누출 되도록 하려고 합니다.Optionally specify -Fault to intentionally attempt to leak the handle by aborting the thread in a certain window. Windows Perfmon.exe 도구를 사용 하 여 오류를 삽입 하는 동안 핸들 수를 모니터링 합니다.Use the Windows Perfmon.exe tool to monitor handle counts while injecting faults.

using System;
using System.Runtime.InteropServices;
using System.IO;
using System.ComponentModel;
using System.Security.Permissions;
using System.Security;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Runtime.ConstrainedExecution;

namespace SafeHandleDemo
{
    [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
    [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
    internal class MySafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        // Create a SafeHandle, informing the base class
        // that this SafeHandle instance "owns" the handle,
        // and therefore SafeHandle should call
        // our ReleaseHandle method when the SafeHandle
        // is no longer in use.
        private MySafeFileHandle()
            : base(true)
        {
        }
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        override protected bool ReleaseHandle()
        {
            // Here, we must obey all rules for constrained execution regions.
            return NativeMethods.CloseHandle(handle);
            // If ReleaseHandle failed, it can be reported via the
            // "releaseHandleFailed" managed debugging assistant (MDA).  This
            // MDA is disabled by default, but can be enabled in a debugger
            // or during testing to diagnose handle corruption problems.
            // We do not throw an exception because most code could not recover
            // from the problem.
        }
    }

    [SuppressUnmanagedCodeSecurity()]
    internal static class NativeMethods
    {
        // Win32 constants for accessing files.
        internal const int GENERIC_READ = unchecked((int)0x80000000);

        // Allocate a file object in the kernel, then return a handle to it.
        [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
        internal extern static MySafeFileHandle CreateFile(String fileName,
           int dwDesiredAccess, System.IO.FileShare dwShareMode,
           IntPtr securityAttrs_MustBeZero, System.IO.FileMode dwCreationDisposition,
           int dwFlagsAndAttributes, IntPtr hTemplateFile_MustBeZero);

        // Use the file handle.
        [DllImport("kernel32", SetLastError = true)]
        internal extern static int ReadFile(MySafeFileHandle handle, byte[] bytes,
           int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

        // Free the kernel's file object (close the file).
        [DllImport("kernel32", SetLastError = true)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        internal extern static bool CloseHandle(IntPtr handle);
    }

    // The MyFileReader class is a sample class that accesses an operating system
    // resource and implements IDisposable. This is useful to show the types of
    // transformation required to make your resource wrapping classes
    // more resilient. Note the Dispose and Finalize implementations.
    // Consider this a simulation of System.IO.FileStream.
    public class MyFileReader : IDisposable
    {
        // _handle is set to null to indicate disposal of this instance.
        private MySafeFileHandle _handle;

        public MyFileReader(String fileName)
        {
            // Security permission check.
            String fullPath = Path.GetFullPath(fileName);
            new FileIOPermission(FileIOPermissionAccess.Read, fullPath).Demand();

            // Open a file, and save its handle in _handle.
            // Note that the most optimized code turns into two processor
            // instructions: 1) a call, and 2) moving the return value into
            // the _handle field.  With SafeHandle, the CLR's platform invoke
            // marshaling layer will store the handle into the SafeHandle
            // object in an atomic fashion. There is still the problem
            // that the SafeHandle object may not be stored in _handle, but
            // the real operating system handle value has been safely stored
            // in a critical finalizable object, ensuring against leaking
            // the handle even if there is an asynchronous exception.

            MySafeFileHandle tmpHandle;
            tmpHandle = NativeMethods.CreateFile(fileName, NativeMethods.GENERIC_READ,
                FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);

            // An async exception here will cause us to run our finalizer with
            // a null _handle, but MySafeFileHandle's ReleaseHandle code will
            // be invoked to free the handle.

            // This call to Sleep, run from the fault injection code in Main,
            // will help trigger a race. But it will not cause a handle leak
            // because the handle is already stored in a SafeHandle instance.
            // Critical finalization then guarantees that freeing the handle,
            // even during an unexpected AppDomain unload.
            Thread.Sleep(500);
            _handle = tmpHandle;  // Makes _handle point to a critical finalizable object.

            // Determine if file is opened successfully.
            if (_handle.IsInvalid)
                throw new Win32Exception(Marshal.GetLastWin32Error(), fileName);
        }

        public void Dispose()  // Follow the Dispose pattern - public nonvirtual.
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // No finalizer is needed. The finalizer on SafeHandle
        // will clean up the MySafeFileHandle instance,
        // if it hasn't already been disposed.
        // Howerver, there may be a need for a subclass to
        // introduce a finalizer, so Dispose is properly implemented here.
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        protected virtual void Dispose(bool disposing)
        {
            // Note there are three interesting states here:
            // 1) CreateFile failed, _handle contains an invalid handle
            // 2) We called Dispose already, _handle is closed.
            // 3) _handle is null, due to an async exception before
            //    calling CreateFile. Note that the finalizer runs
            //    if the constructor fails.
            if (_handle != null && !_handle.IsInvalid)
            {
                // Free the handle
                _handle.Dispose();
            }
            // SafeHandle records the fact that we've called Dispose.
        }


        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public byte[] ReadContents(int length)
        {
            if (_handle.IsInvalid)  // Is the handle disposed?
                throw new ObjectDisposedException("FileReader is closed");

            // This sample code will not work for all files.
            byte[] bytes = new byte[length];
            int numRead = 0;
            int r = NativeMethods.ReadFile(_handle, bytes, length, out numRead, IntPtr.Zero);
            // Since we removed MyFileReader's finalizer, we no longer need to
            // call GC.KeepAlive here.  Platform invoke will keep the SafeHandle
            // instance alive for the duration of the call.
            if (r == 0)
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (numRead < length)
            {
                byte[] newBytes = new byte[numRead];
                Array.Copy(bytes, newBytes, numRead);
                bytes = newBytes;
            }
            return bytes;
        }
    }

    static class Program
    {
        // Testing harness that injects faults.
        private static bool _printToConsole = false;
        private static bool _workerStarted = false;

        private static void Usage()
        {
            Console.WriteLine("Usage:");
            // Assumes that application is named HexViwer"
            Console.WriteLine("HexViewer <fileName> [-fault]");
            Console.WriteLine(" -fault Runs hex viewer repeatedly, injecting faults.");
        }

        private static void ViewInHex(Object fileName)
        {
            _workerStarted = true;
            byte[] bytes;
            using (MyFileReader reader = new MyFileReader((String)fileName))
            {
                bytes = reader.ReadContents(20);
            }  // Using block calls Dispose() for us here.

            if (_printToConsole)
            {
                // Print up to 20 bytes.
                int printNBytes = Math.Min(20, bytes.Length);
                Console.WriteLine("First {0} bytes of {1} in hex", printNBytes, fileName);
                for (int i = 0; i < printNBytes; i++)
                    Console.Write("{0:x} ", bytes[i]);
                Console.WriteLine();
            }
        }

        static void Main(string[] args)
        {
            if (args.Length == 0 || args.Length > 2 ||
                args[0] == "-?" || args[0] == "/?")
            {
                Usage();
                return;
            }

            String fileName = args[0];
            bool injectFaultMode = args.Length > 1;
            if (!injectFaultMode)
            {
                _printToConsole = true;
                ViewInHex(fileName);
            }
            else
            {
                Console.WriteLine("Injecting faults - watch handle count in perfmon (press Ctrl-C when done)");
                int numIterations = 0;
                while (true)
                {
                    _workerStarted = false;
                    Thread t = new Thread(new ParameterizedThreadStart(ViewInHex));
                    t.Start(fileName);
                    Thread.Sleep(1);
                    while (!_workerStarted)
                    {
                        Thread.Sleep(0);
                    }
                    t.Abort();  // Normal applications should not do this.
                    numIterations++;
                    if (numIterations % 10 == 0)
                        GC.Collect();
                    if (numIterations % 10000 == 0)
                        Console.WriteLine(numIterations);
                }
            }

        }
    }
}

설명

SafeHandle 클래스는 리소스 핸들 중간 가비지 수집에 의해 회수에서 의도 하지 않은 관리 되지 않는 개체를 참조 하는 Windows에서 재생 되 고 처리를 방지의 중요 한 종료를 제공 합니다.The SafeHandle class provides critical finalization of handle resources, preventing handles from being reclaimed prematurely by garbage collection and from being recycled by Windows to reference unintended unmanaged objects.

이 항목에는 다음 단원이 포함되어 있습니다.This topic includes the following sections:

이유는 SafeHandle? Why SafeHandle?
SafeHandle의 용도 What SafeHandle does
SafeHandle에서 파생 된 클래스Classes derived from SafeHandle

이유는 SafeHandle?Why SafeHandle?

.NET Framework 버전 2.0에서는 모든 운영 체제 핸들의 캡슐화 할 수 있었습니다 하기 전에 IntPtr 관리 되는 래퍼 개체입니다.Before the .NET Framework version 2.0, all operating system handles could only be encapsulated in the IntPtr managed wrapper object. 네이티브 코드와 상호 운용 하는 편리한 방법을 었지만, 핸들 스레드 예기치 않게 중단 또는 스택 오버플로와 같은 비동기 예외에 의해 누수 될 수 있습니다.While this was a convenient way to interoperate with native code, handles could be leaked by asynchronous exceptions, such as a thread aborting unexpectedly or a stack overflow. 이러한 비동기 예외는 운영 체제 리소스를 정리 하는 데 방해가 되며 응용 프로그램에서 거의 모든 곳에서 발생할 수 있습니다.These asynchronous exceptions are an obstacle to cleaning up operating system resources, and they can occur almost anywhere in your app.

하지만에 대 한 재정의 Object.Finalize 메서드는 개체가 가비지 수집에 따라서는 될 때 관리 되지 않는 리소스 정리를 허용, 플랫폼 내에서 메서드를 실행 하는 동안 가비지 수집에 의해 종료 가능한 개체를 회수할 수 있습니다 호출 호출 합니다.Although overrides to the Object.Finalize method allow cleanup of unmanaged resources when an object is being garbage collected, in some circumstances, finalizable objects can be reclaimed by garbage collection while executing a method within a platform invoke call. 종료자에는 해당 플랫폼에 전달 된 핸들을 해제 하는 경우 호출, 손상 처리 될 수 있습니다.If a finalizer frees the handle passed to that platform invoke call, it could lead to handle corruption. 플랫폼을 하는 동안 메서드가 차단 된 동안 핸들을 회수할 수도 파일을 읽을 때와 같은 호출을 호출 합니다.The handle could also be reclaimed while your method is blocked during a platform invoke call, such as while reading a file.

보다 Windows 핸들을 적극적으로 재활용 하기 때문에 대 한 핸들 수 재활용 될 되 고 중요 한 데이터를 포함할 수 있는 다른 리소스를 가리킵니다.More critically, because Windows aggressively recycles handles, a handle could be recycled and point to another resource that might contain sensitive data. 이 재생 공격으로 알려져 있습니다 잠재적으로 데이터를 손상 및 보안 위협이 될 합니다.This is known as a recycle attack and can potentially corrupt data and be a security threat.

SafeHandle의 용도What SafeHandle does

SafeHandle 클래스는 다양 한 이러한 개체 수명 문제를 단순화 하 고는 플랫폼과 통합 되어 운영 체제 리소스가 누수 되지 않도록 호출 합니다.The SafeHandle class simplifies several of these object lifetime issues, and is integrated with platform invoke so that operating system resources are not leaked. SafeHandle 클래스 할당 및 중단 없이 핸들을 해제 하 여 개체 수명 문제를 해결 합니다.The SafeHandle class resolves object lifetime issues by assigning and releasing handles without interruption. 중요 종료자는 핸들이 닫히고 하는 동안 예기치 않은 실행이 보장 되도록 포함 AppDomain 언로드의 경우에 플랫폼 호출을 호출 하는 경우에도 손상된 된 상태에서 것으로 간주 됩니다.It contains a critical finalizer that ensures that the handle is closed and is guaranteed to run during unexpected AppDomain unloads, even in cases when the platform invoke call is assumed to be in a corrupted state.

때문에 SafeHandle 에서 상속 CriticalFinalizerObject, 중요 종료자 하기 전에 중요 하지 않은 모든 종료 자가 호출 됩니다.Because SafeHandle inherits from CriticalFinalizerObject, all the noncritical finalizers are called before any of the critical finalizers. 종료 자가 동일한 가비지 컬렉션 중 라이브 더 이상 없는 개체에서 호출 됩니다.The finalizers are called on objects that are no longer live during the same garbage collection pass. 예를 들어, 한 FileStream 개체 핸들이 누출 되거나 재생 될 위험 없이 기존 버퍼링 된 데이터를 찾아내기 위해 일반 종료자를 실행할 수 있습니다.For example, a FileStream object can run a normal finalizer to flush out existing buffered data without the risk of the handle being leaked or recycled. 일반 사용에 대 한 위험 및 중요 하지 않은 종료자 간에 매우 약하게 만드는이 순서가 없습니다.This very weak ordering between critical and noncritical finalizers is not intended for general use. 사용 하 여 해당 라이브러리를 허용 하 여 기존 라이브러리의 마이그레이션을 지원 하기 위해 주로 존재 SafeHandle 해당 의미 체계를 변경 하지 않고 있습니다.It exists primarily to assist in the migration of existing libraries by allowing those libraries to use SafeHandle without altering their semantics. 또한 중요 종료자 및 작업 호출, 같은 SafeHandle.ReleaseHandle() 메서드에 제약이 있는 실행 영역에 있어야 합니다.Additionally, the critical finalizer and anything it calls, such as the SafeHandle.ReleaseHandle() method, must be in a constrained execution region. 이 인해 제약 조건에는 종료 자가 호출 그래프 내에서 코드를 작성할 수 있습니다.This imposes constraints on what code can be written within the finalizer's call graph.

플랫폼 작업을 자동으로 호출에 의해 캡슐화 되는 핸들의 참조 횟수를 증가 SafeHandle 및 완료 되 면 감소 합니다.Platform invoke operations automatically increment the reference count of handles encapsulated by a SafeHandle and decrement them upon completion. 이렇게 하면 핸들 됩니다 하지 수 재활용 되거나 예기치 않게 닫았습니다.This ensures that the handle will not be recycled or closed unexpectedly.

생성할 때 기본 핸들의 소유권을 지정할 수 있습니다 SafeHandle 값을 제공 하 여 개체를 ownsHandle 인수에는 SafeHandle 클래스 생성자입니다.You can specify ownership of the underlying handle when constructing SafeHandle objects by supplying a value to the ownsHandle argument in the SafeHandle class constructor. 이 제어 하는지 여부를 SafeHandle 개체가 삭제 된 후 개체 핸들을 해제 합니다.This controls whether the SafeHandle object will release the handle after the object has been disposed. 특이 한 수명 요구 사항을 핸들 또는 수명이 다른 사용자에 의해 제어 되는 핸들을 사용 하는 것에 대 한 유용 합니다.This is useful for handles with peculiar lifetime requirements or for consuming a handle whose lifetime is controlled by someone else.

SafeHandle에서 파생 된 클래스Classes derived from SafeHandle

SafeHandle 운영 체제 핸들에 대 한 추상 래퍼 클래스가입니다.SafeHandle is an abstract wrapper class for operating system handles. 이 클래스로부터 파생은 어렵습니다.Deriving from this class is difficult. 대신 Microsoft.Win32.SafeHandles 네임스페이스에서 다음에 대해 SafeHandle을 제공하는 파생된 클래스를 사용하십시오.Instead, use the derived classes in the Microsoft.Win32.SafeHandles namespace that provide safe handles for the following:

구현자 참고

파생 된 클래스를 만들려면 SafeHandle를 만들고 운영 체제 핸들을 해제 하는 방법을 알고 있어야 합니다.To create a class derived from SafeHandle, you must know how to create and free an operating system handle. 이 프로세스는 몇 가지 사용 하기 때문에 다른 핸들 형식에 대 한 다른 합니다 CloseHandle 함수를 같은 보다 구체적인 함수를 사용 하기도 unmapviewoffile이 또는 FindClose.This process is different for different handle types because some use the CloseHandle function, while others use more specific functions such as UnmapViewOfFile or FindClose. 파생된 클래스의 이러한 이유로 만들어야 SafeHandle 래핑할 안전한 핸들을 원하는 각 운영 체제 핸들 형식에 대 한 합니다.For this reason, you must create a derived class of SafeHandle for each operating system handle type that you want to wrap in a safe handle.

SafeHandle에서 상속하는 경우 IsInvalidReleaseHandle() 멤버를 재정의해야 합니다.When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle().

또한 잘못 된 핸들 값을 나타내는 값을 사용 하 여 기본 생성자를 호출 하는 매개 변수가 없는 생성자를 제공 Boolean 하 고, 네이티브 핸들이에서 SafeHandle 소유 하 고 있는지 여부를 나타내는 값을 제공 해야 합니다. SafeHandle 삭제 된 경우You should also provide a parameterless constructor that calls the base constructor with a value that represent an invalid handle value, and a Boolean value indicating whether the native handle is owned by the SafeHandle and consequently should be freed when that SafeHandle has been disposed.

생성자

SafeHandle()
SafeHandle(IntPtr, Boolean)

지정된 잘못된 핸들 값을 사용하여 SafeHandle 클래스의 새 인스턴스를 초기화합니다.Initializes a new instance of the SafeHandle class with the specified invalid handle value.

필드

handle

래핑할 핸들을 지정합니다.Specifies the handle to be wrapped.

속성

IsClosed

핸들이 닫혔는지 여부를 나타내는 값을 가져옵니다.Gets a value indicating whether the handle is closed.

IsInvalid

파생 클래스에서 재정의된 경우 핸들 값이 잘못되었는지 여부를 나타내는 값을 가져옵니다.When overridden in a derived class, gets a value indicating whether the handle value is invalid.

메서드

Close()

핸들의 리소스를 해제하도록 표시합니다.Marks the handle for releasing and freeing resources.

DangerousAddRef(Boolean)

SafeHandle 인스턴스의 참조 카운터의 값을 수동으로 증가시킵니다.Manually increments the reference counter on SafeHandle instances.

DangerousGetHandle()

handle 필드의 값을 반환합니다.Returns the value of the handle field.

DangerousRelease()

SafeHandle 인스턴스의 참조 카운터의 값을 수동으로 감소시킵니다.Manually decrements the reference counter on a SafeHandle instance.

Dispose()

SafeHandle 클래스에서 사용하는 모든 리소스를 해제합니다.Releases all resources used by the SafeHandle class.

Dispose(Boolean)

일반적인 삭제 작업을 수행할지 여부를 지정하여 SafeHandle 클래스에서 사용하는 관리되지 않는 리소스를 해제합니다.Releases the unmanaged resources used by the SafeHandle class specifying whether to perform a normal dispose operation.

Equals(Object)

지정한 개체와 현재 개체가 같은지 여부를 확인합니다.Determines whether the specified object is equal to the current object.

(다음에서 상속됨 Object)
Finalize()

핸들에 연결된 모든 리소스를 해제합니다.Frees all resources associated with the handle.

GetHashCode()

기본 해시 함수로 작동합니다.Serves as the default hash function.

(다음에서 상속됨 Object)
GetType()

현재 인스턴스의 Type을 가져옵니다.Gets the Type of the current instance.

(다음에서 상속됨 Object)
MemberwiseClone()

현재 Object의 단순 복사본을 만듭니다.Creates a shallow copy of the current Object.

(다음에서 상속됨 Object)
ReleaseHandle()

파생 클래스에서 재정의된 경우 핸들을 해제하는 데 필요한 코드를 실행합니다.When overridden in a derived class, executes the code required to free the handle.

SetHandle(IntPtr)

지정된 기존 핸들에 대한 핸들을 설정합니다.Sets the handle to the specified pre-existing handle.

SetHandleAsInvalid()

더 이상 사용되지 않는 핸들로 표시합니다.Marks a handle as no longer used.

ToString()

현재 개체를 나타내는 문자열을 반환합니다.Returns a string that represents the current object.

(다음에서 상속됨 Object)

보안

InheritanceDemand
상속자의 경우 완전히 신뢰합니다.for full trust for inheritors. 이 멤버는 부분적으로 신뢰할 수 있는 코드에서 상속할 수 없습니다.This member cannot be inherited by partially trusted code.

SecurityCriticalAttribute
직접 실행 호출자에 대 한 완전 신뢰가 필요 합니다.requires full trust for the immediate caller. 이 멤버는 부분적으로 신뢰할 수 있는 또는 투명 코드에서 사용할 수 없습니다.This member cannot be used by partially trusted or transparent code.

적용 대상

추가 정보