Object.Finalize 메서드

정의

가비지 컬렉션이 회수하기 전에 개체가 리소스를 해제하고 다른 정리 작업을 수행할 수 있게 합니다.Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.

!Object ()
~Object ();
abstract member Finalize : unit -> unit
override this.Finalize : unit -> unit
Finalize ()

예제

다음 예제에서는 Finalize를 재정의 하는 개체가 제거 될 때 Finalize 메서드가 호출 되는지 확인 합니다.The following example verifies that the Finalize method is called when an object that overrides Finalize is destroyed. 애플리케이션을 프로덕션에서의 Finalize 개체에서 보유 하는 관리 되지 않는 리소스를 해제 메서드를 재정의 해야 합니다.Note that, in a production application, the Finalize method would be overridden to release unmanaged resources held by the object. 또한이 C# 예제에서는 Finalize 메서드를 재정의 하는 대신 소멸자를 제공 합니다.Also note that the C# example provides a destructor instead of overriding the Finalize method.

using System;
using System.Diagnostics;

public class ExampleClass
{
   Stopwatch sw;

   public ExampleClass()
   {
      sw = Stopwatch.StartNew();
      Console.WriteLine("Instantiated object");
   }

   public void ShowDuration()
   {
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        this, sw.Elapsed);
   }

   ~ExampleClass()
   {
      Console.WriteLine("Finalizing object");
      sw.Stop();
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        this, sw.Elapsed);
   }
}

public class Demo
{
   public static void Main()
   {
      ExampleClass ex = new ExampleClass();
      ex.ShowDuration();
   }
}
// The example displays output like the following:
//    Instantiated object
//    This instance of ExampleClass has been in existence for 00:00:00.0011060
//    Finalizing object
//    This instance of ExampleClass has been in existence for 00:00:00.0036294
Imports System.Diagnostics

Public Class ExampleClass
   Dim sw As StopWatch
   
   Public Sub New()
      sw = Stopwatch.StartNew()
      Console.WriteLine("Instantiated object")
   End Sub 

   Public Sub ShowDuration()
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        Me, sw.Elapsed)
   End Sub
   
   Protected Overrides Sub Finalize()
      Console.WriteLine("Finalizing object")
      sw.Stop()
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        Me, sw.Elapsed)
   End Sub
End Class

Module Demo
   Public Sub Main()
      Dim ex As New ExampleClass()
      ex.ShowDuration()
   End Sub
End Module
' The example displays output like the following:
'    Instantiated object
'    This instance of ExampleClass has been in existence for 00:00:00.0011060
'    Finalizing object
'    This instance of ExampleClass has been in existence for 00:00:00.0036294

Finalize 메서드를 재정의 하는 추가 예제는 GC.SuppressFinalize 메서드를 참조 하세요.For an additional example that overrides the Finalize method, see the GC.SuppressFinalize method.

설명

Finalize 메서드는 개체가 제거 되기 전에 현재 개체가 보유 하 고 있는 관리 되지 않는 리소스에서 정리 작업을 수행 하는 데 사용 됩니다.The Finalize method is used to perform cleanup operations on unmanaged resources held by the current object before the object is destroyed. 메서드는 보호 되 고 이므로이 클래스 또는 파생된 클래스를 통해 액세스할 수 있습니다.The method is protected and therefore is accessible only through this class or through a derived class.

이 섹션에서는 다음 작업을 수행합니다.In this section:

종료의 작동 원리How finalization works

Object 클래스는 Finalize 메서드에 대 한 구현을 제공 하지 않으며, 가비지 수집기는 Finalize 메서드를 재정의 하지 않는 한 종료에 대해 Object에서 파생 된 형식을 표시 하지 않습니다.The Object class provides no implementation for the Finalize method, and the garbage collector does not mark types derived from Object for finalization unless they override the Finalize method.

형식이 Finalize 메서드를 재정의 하는 경우 가비지 수집기는 형식의 각 인스턴스에 대 한 항목을 종료 큐 라는 내부 구조에 추가 합니다.If a type does override the Finalize method, the garbage collector adds an entry for each instance of the type to an internal structure called the finalization queue. 가비지 수집기가 메모리를 회수할 수 전에 종료 코드를 실행 해야 관리 되는 힙에 있는 모든 개체에 대 한 항목을 포함 하는 종료 큐에 추가 합니다.The finalization queue contains entries for all the objects in the managed heap whose finalization code must run before the garbage collector can reclaim their memory. 그러면 가비지 수집기는 다음 조건에 따라 Finalize 메서드를 자동으로 호출 합니다.The garbage collector then calls the Finalize method automatically under the following conditions:

  • GC.SuppressFinalize 메서드를 호출 하 여 개체가 종료에서 제외 되지 않은 경우에는 가비지 수집기가 개체에 액세스할 수 없다는 것을 발견 한 것입니다.After the garbage collector has discovered that an object is inaccessible, unless the object has been exempted from finalization by a call to the GC.SuppressFinalize method.

  • .NET framework만, 애플리케이션 도메인을 종료 하는 동안 없으면 개체를 종료 한 예외로 지정 합니다.On .NET Framework only, during shutdown of an application domain, unless the object is exempt from finalization. 종료 하는 동안에 개체 계속 액세스할 수 있는 종료 됩니다.During shutdown, even objects that are still accessible are finalized.

GC.ReRegisterForFinalize와 같은 메커니즘을 사용 하 여 개체를 다시 등록 하 고 GC.SuppressFinalize 메서드를 이후에 호출 하지 않는 한 Finalize는 지정 된 인스턴스에서 한 번만 자동으로 호출 됩니다.Finalize is automatically called only once on a given instance, unless the object is re-registered by using a mechanism such as GC.ReRegisterForFinalize and the GC.SuppressFinalize method has not been subsequently called.

Finalize 작업에는 다음과 같은 제한 사항이 있습니다.Finalize operations have the following limitations:

  • 정확한 시간 종료 자가 실행 되는 경우 정의 되지 않습니다.The exact time when the finalizer executes is undefined. 클래스 인스턴스에 대 한 리소스의 결정적 릴리스를 보장 하려면 Close 메서드를 구현 하거나 IDisposable.Dispose 구현을 제공 합니다.To ensure deterministic release of resources for instances of your class, implement a Close method or provide a IDisposable.Dispose implementation.

  • 다른 하나의 개체가 참조 하는 경우에 특정 순서에 관계 없이 실행 하는 두 개체의 종료자 보장 되지 않습니다.The finalizers of two objects are not guaranteed to run in any specific order, even if one object refers to the other. 즉, 둘 다 종료자 개체 A가 개체 B에 대 한 참조를 개체 B 수 이미 종료 되었을 개체의 종료자를 시작할 때입니다.That is, if Object A has a reference to Object B and both have finalizers, Object B might have already been finalized when the finalizer of Object A starts.

  • 종료 자가 실행 되는 스레드가 지정 되지 않습니다.The thread on which the finalizer runs is unspecified.

Finalize 메서드는 완료 될 때까지 실행 되지 않거나 다음과 같은 예외 상황에서 실행 되지 않을 수 있습니다.The Finalize method might not run to completion or might not run at all under the following exceptional circumstances:

  • 다른 종료자를 무기한으로 차단 하는 경우 (잠금이 되지를 고 수 등을 가져오려고 시도 무한 루프로 이동).If another finalizer blocks indefinitely (goes into an infinite loop, tries to obtain a lock it can never obtain, and so on). 런타임에서 종료자 실행이 완료 하려고 하기 때문에 다른 종료자 수 호출할 수 없습니다 경우 종료자 블록 무기한.Because the runtime tries to run finalizers to completion, other finalizers might not be called if a finalizer blocks indefinitely.

  • 경우 런타임에서 정리할 기회를 제공 하지 않고 프로세스가 종료 됩니다.If the process terminates without giving the runtime a chance to clean up. 이 경우 종료 프로세스의 첫 번째 알림 런타임의 DLL_PROCESS_DETACH 알림을 경우합니다In this case, the runtime's first notification of process termination is a DLL_PROCESS_DETACH notification.

런타임에서 종료 가능한 개체 수가 계속 감소 하는 동안에 개체를 종료 하는 동안 종료를 계속 합니다.The runtime continues to finalize objects during shutdown only while the number of finalizable objects continues to decrease.

하는 경우 Finalize 또는 재정의 Finalize 예외를 throw 하 고 런타임이 기본 정책을 재정의 하는 애플리케이션에서 호스트 되지 않는, 프로세스 및 없는 활성 런타임이 종료 try/finally 블록 또는 종료자 실행 됩니다.If Finalize or an override of Finalize throws an exception, and the runtime is not hosted by an application that overrides the default policy, the runtime terminates the process and no active try/finally blocks or finalizers are executed. 이 동작은 종료자 없거나 사용 가능한 리소스를 삭제 하는 경우 프로세스의 무결성을 보장 합니다.This behavior ensures process integrity if the finalizer cannot free or destroy resources.

Finalize 메서드 재정의Overriding the Finalize method

가비지 수집 중에이를 사용 하는 관리 되는 개체가 삭제 될 때 해제 해야 하는 파일 핸들 또는 데이터베이스 연결과 같이 관리 되지 않는 리소스를 사용 하는 클래스에 대 한 Finalize를 재정의 해야 합니다.You should override Finalize for a class that uses unmanaged resources, such as file handles or database connections that must be released when the managed object that uses them is discarded during garbage collection. 가비지 수집기는 관리 되는 리소스를 자동으로 해제 하므로 관리 되는 개체에 대 한 Finalize 메서드를 구현 하지 않아야 합니다.You shouldn't implement a Finalize method for managed objects because the garbage collector releases managed resources automatically.

중요

관리 되지 않는 리소스를 래핑하는 SafeHandle 개체를 사용할 수 있는 경우에는 safe 핸들로 dispose 패턴을 구현 하 고 Finalize재정의 하지 않는 것이 좋습니다.If a SafeHandle object is available that wraps your unmanaged resource, the recommended alternative is to implement the dispose pattern with a safe handle and not override Finalize. 자세한 내용은 SafeHandle 대체 섹션을 참조 하세요.For more information, see The SafeHandle alternative section.

Object.Finalize 메서드는 기본적으로 아무 작업도 수행 하지 않지만 필요한 경우에만 Finalize를 재정의 하 고 관리 되지 않는 리소스를 해제 해야 합니다.The Object.Finalize method does nothing by default, but you should override Finalize only if necessary, and only to release unmanaged resources. 메모리를 회수 길어질 종료 작업을 실행 하는 경우 두 개 이상의 가비지 컬렉션을 필요로 하는 경향이 있습니다.Reclaiming memory tends to take much longer if a finalization operation runs, because it requires at least two garbage collections. 또한 참조 형식에 대해서만 Finalize 메서드를 재정의 해야 합니다.In addition, you should override the Finalize method for reference types only. 공용 언어 런타임에서 참조 형식만 종료합니다.The common language runtime only finalizes reference types. 값 형식에 종료자는 무시합니다.It ignores finalizers on value types.

Object.Finalize 메서드의 범위가 protected됩니다.The scope of the Object.Finalize method is protected. 클래스에서 메서드를 재정의 하는 경우에이 제한 된 범위를 유지 관리 해야 합니다.You should maintain this limited scope when you override the method in your class. 유지 하 여는 Finalize 개체의 호출에서 애플리케이션의 사용자를 방지 보호 되는 메서드, Finalize 메서드를 직접.By keeping a Finalize method protected, you prevent users of your application from calling an object's Finalize method directly.

파생 형식에서 Finalize의 모든 구현은 기본 형식의 Finalize구현을 호출 해야 합니다.Every implementation of Finalize in a derived type must call its base type's implementation of Finalize. 이 애플리케이션에서 코드를 호출할 수는 유일한 경우는 Finalize합니다.This is the only case in which application code is allowed to call Finalize. 개체의 Finalize 메서드는 기본 클래스의 개체와 다른 개체에 대해 메서드를 호출 하면 안 됩니다.An object's Finalize method shouldn't call a method on any objects other than that of its base class. 호출 되는 다른 개체 수집할 수 있는 동시 호출 하는 개체와 같은 공용 언어 런타임 종료의 경우 때문입니다.This is because the other objects being called could be collected at the same time as the calling object, such as in the case of a common language runtime shutdown.

참고

C# 컴파일러에서는 Finalize 메서드를 재정의할 수 없습니다.The C# compiler does not allow you to override the Finalize method. 대신 클래스에 대 한 소멸자 를 구현 하 여 종료자를 제공 합니다.Instead, you provide a finalizer by implementing a destructor for your class. C# 소멸자는 자동으로 기본 클래스의 소멸자를 호출합니다.A C# destructor automatically calls the destructor of its base class.

또한 C++ 시각적 개체는 Finalize 메서드를 구현 하기 위한 자체 구문을 제공 합니다.Visual C++ also provides its own syntax for implementing the Finalize method. 자세한 내용은 방법: 클래스 및 구조체 정의 및 사용C++(/cli)의 "소멸자 및 종료자" 섹션을 참조 하세요.For more information, see the "Destructors and finalizers" section of How to: Define and Consume Classes and Structs (C++/CLI).

가비지 컬렉션에서는 비결 이기 때문에 모르는 가비지 수집기가 종료를 수행 하는 경우에 정확 하 게 합니다.Because garbage collection is non-deterministic, you do not know precisely when the garbage collector performs finalization. 리소스를 즉시 해제 하려면 dispose 패턴과 IDisposable 인터페이스를 구현 하도록 선택할 수도 있습니다.To release resources immediately, you can also choose to implement the dispose pattern and the IDisposable interface. 클래스의 소비자는 IDisposable.Dispose 구현을 호출 하 여 관리 되지 않는 리소스를 해제할 수 있으며, Finalize 메서드를 사용 하 여 Dispose 메서드가 호출 되지 않는 경우 관리 되지 않는 리소스를 해제할 수 있습니다.The IDisposable.Dispose implementation can be called by consumers of your class to free unmanaged resources, and you can use the Finalize method to free unmanaged resources in the event that the Dispose method is not called.

가비지 수집 중에 개체를 정리 하 고 나 서 개체를 다시 액세스할 수 있도록 하는 resurrecting을 포함 하 여 거의 모든 작업을 수행할 수 Finalize.Finalize can take almost any action, including resurrecting an object (that is, making the object accessible again) after it has been cleaned up during garbage collection. 그러나 개체는 한 번만 resurrected 수 있습니다. 가비지 수집 중에는 resurrected 개체에 대해 Finalize를 호출할 수 없습니다.However, the object can only be resurrected once; Finalize cannot be called on resurrected objects during garbage collection.

SafeHandle 대안The SafeHandle alternative

신뢰할 수 있는 종료자를 만들기 어렵습니다, 이므로 같은 시스템 예외를 처리 되지 않은 애플리케이션의 상태에 대 한 가정을 변경할 수 없으므로 OutOfMemoryExceptionStackOverflowException 종료자를 종료 합니다.Creating reliable finalizers is often difficult, because you cannot make assumptions about the state of your application, and because unhandled system exceptions such as OutOfMemoryException and StackOverflowException terminate the finalizer. 관리 되지 않는 리소스를 해제 하기 위해 클래스에 대 한 종료자를 구현 하는 대신 System.Runtime.InteropServices.SafeHandle 클래스에서 파생 된 개체를 사용 하 여 관리 되지 않는 리소스를 래핑한 다음 종료자 없이 dispose 패턴을 구현할 수 있습니다.Instead of implementing a finalizer for your class to release unmanaged resources, you can use an object that is derived from the System.Runtime.InteropServices.SafeHandle class to wrap your unmanaged resources, and then implement the dispose pattern without a finalizer. .NET Framework은 System.Runtime.InteropServices.SafeHandle에서 파생 된 Microsoft.Win32 네임 스페이스에 다음 클래스를 제공 합니다.The .NET Framework provides the following classes in the Microsoft.Win32 namespace that are derived from System.Runtime.InteropServices.SafeHandle:

다음 예제에서는 Finalize 메서드를 재정의 하는 대신 safehandle을 사용 하는 dispose 패턴 을 사용 합니다.The following example uses the dispose pattern with safe handles instead of overriding the Finalize method. 정의 FileAssociation 특정 파일 확장명을 사용 하 여 파일을 처리 하는 애플리케이션에 대 한 레지스트리 정보를 래핑하는 클래스입니다.It defines a FileAssociation class that wraps registry information about the application that handles files with a particular file extension. Windows RegOpenKeyEx 함수 호출에서 out 매개 변수로 반환 되는 두 레지스트리 핸들은 SafeRegistryHandle 생성자에 전달 됩니다.The two registry handles returned as out parameters by Windows RegOpenKeyEx function calls are passed to the SafeRegistryHandle constructor. 그러면 형식의 protected Dispose 메서드가 SafeRegistryHandle.Dispose 메서드를 호출 하 여 이러한 두 핸들을 해제 합니다.The type's protected Dispose method then calls the SafeRegistryHandle.Dispose method to free these two handles.

using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;

public class FileAssociationInfo : IDisposable
{
   // Private variables.
   private String ext;
   private String openCmd;
   private String args;
   private SafeRegistryHandle hExtHandle, hAppIdHandle;

   // Windows API calls.
   [DllImport("advapi32.dll", CharSet= CharSet.Auto, SetLastError=true)]
   private static extern int RegOpenKeyEx(IntPtr hKey,
                  String lpSubKey, int ulOptions, int samDesired,
                  out IntPtr phkResult);
   [DllImport("advapi32.dll", CharSet= CharSet.Unicode, EntryPoint = "RegQueryValueExW",
              SetLastError=true)]
   private static extern int RegQueryValueEx(IntPtr hKey,
                  string lpValueName, int lpReserved, out uint lpType,
                  string lpData, ref uint lpcbData);
   [DllImport("advapi32.dll", SetLastError = true)]
   private static extern int RegSetValueEx(IntPtr hKey, [MarshalAs(UnmanagedType.LPStr)] string lpValueName,
                  int Reserved, uint dwType, [MarshalAs(UnmanagedType.LPStr)] string lpData,
                  int cpData);
   [DllImport("advapi32.dll", SetLastError=true)]
   private static extern int RegCloseKey(UIntPtr hKey);

   // Windows API constants.
   private const int HKEY_CLASSES_ROOT = unchecked((int) 0x80000000);
   private const int ERROR_SUCCESS = 0;

    private const int KEY_QUERY_VALUE = 1;
    private const int KEY_SET_VALUE = 0x2;

   private const uint REG_SZ = 1;

   private const int MAX_PATH = 260;

   public FileAssociationInfo(String fileExtension)
   {
      int retVal = 0;
      uint lpType = 0;

      if (!fileExtension.StartsWith("."))
             fileExtension = "." + fileExtension;
      ext = fileExtension;

      IntPtr hExtension = IntPtr.Zero;
      // Get the file extension value.
      retVal = RegOpenKeyEx(new IntPtr(HKEY_CLASSES_ROOT), fileExtension, 0, KEY_QUERY_VALUE, out hExtension);
      if (retVal != ERROR_SUCCESS)
         throw new Win32Exception(retVal);
      // Instantiate the first SafeRegistryHandle.
      hExtHandle = new SafeRegistryHandle(hExtension, true);

      string appId = new string(' ', MAX_PATH);
      uint appIdLength = (uint) appId.Length;
      retVal = RegQueryValueEx(hExtHandle.DangerousGetHandle(), String.Empty, 0, out lpType, appId, ref appIdLength);
      if (retVal != ERROR_SUCCESS)
         throw new Win32Exception(retVal);
      // We no longer need the hExtension handle.
      hExtHandle.Dispose();

      // Determine the number of characters without the terminating null.
      appId = appId.Substring(0, (int) appIdLength / 2 - 1) + @"\shell\open\Command";

      // Open the application identifier key.
      string exeName = new string(' ', MAX_PATH);
      uint exeNameLength = (uint) exeName.Length;
      IntPtr hAppId;
      retVal = RegOpenKeyEx(new IntPtr(HKEY_CLASSES_ROOT), appId, 0, KEY_QUERY_VALUE | KEY_SET_VALUE,
                            out hAppId);
       if (retVal != ERROR_SUCCESS)
         throw new Win32Exception(retVal);

      // Instantiate the second SafeRegistryHandle.
      hAppIdHandle = new SafeRegistryHandle(hAppId, true);

      // Get the executable name for this file type.
      string exePath = new string(' ', MAX_PATH);
      uint exePathLength = (uint) exePath.Length;
      retVal = RegQueryValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0, out lpType, exePath, ref exePathLength);
      if (retVal != ERROR_SUCCESS)
         throw new Win32Exception(retVal);

      // Determine the number of characters without the terminating null.
      exePath = exePath.Substring(0, (int) exePathLength / 2 - 1);
      // Remove any environment strings.
      exePath = Environment.ExpandEnvironmentVariables(exePath);

      int position = exePath.IndexOf('%');
      if (position >= 0) {
         args = exePath.Substring(position);
         // Remove command line parameters ('%0', etc.).
         exePath = exePath.Substring(0, position).Trim();
      }
      openCmd = exePath;
   }

   public String Extension
   { get { return ext; } }

   public String Open
   { get { return openCmd; }
     set {
        if (hAppIdHandle.IsInvalid | hAppIdHandle.IsClosed)
           throw new InvalidOperationException("Cannot write to registry key.");
        if (! File.Exists(value)) {
           string message = String.Format("'{0}' does not exist", value);
           throw new FileNotFoundException(message);
        }
        string cmd = value + " %1";
        int retVal = RegSetValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0,
                                   REG_SZ, value, value.Length + 1);
        if (retVal != ERROR_SUCCESS)
           throw new Win32Exception(retVal);
     } }

   public void Dispose()
   {
      Dispose(true);
      GC.SuppressFinalize(this);
   }

   protected void Dispose(bool disposing)
   {
      // Ordinarily, we release unmanaged resources here;
      // but all are wrapped by safe handles.

      // Release disposable objects.
      if (disposing) {
         if (hExtHandle != null) hExtHandle.Dispose();
         if (hAppIdHandle != null) hAppIdHandle.Dispose();
      }
   }
}
Imports Microsoft.Win32.SafeHandles
Imports System.ComponentModel
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text

Public Class FileAssociationInfo : Implements IDisposable
   ' Private variables.
   Private ext As String
   Private openCmd As String
   Private args As String
   Private hExtHandle, hAppIdHandle As SafeRegistryHandle

   ' Windows API calls.
   Private Declare Unicode Function RegOpenKeyEx Lib"advapi32.dll" _
                   Alias "RegOpenKeyExW" (hKey As IntPtr, lpSubKey As String, _
                   ulOptions As Integer, samDesired As Integer, _
                   ByRef phkResult As IntPtr) As Integer
   Private Declare Unicode Function RegQueryValueEx Lib "advapi32.dll" _
                   Alias "RegQueryValueExW" (hKey As IntPtr, _
                   lpValueName As String, lpReserved As Integer, _
                   ByRef lpType As UInteger, lpData As String, _
                   ByRef lpcbData As UInteger) As Integer   
   Private Declare Function RegSetValueEx Lib "advapi32.dll" _
                  (hKey As IntPtr, _
                  <MarshalAs(UnmanagedType.LPStr)> lpValueName As String, _
                  reserved As Integer, dwType As UInteger, _
                  <MarshalAs(UnmanagedType.LPStr)> lpData As String, _
                  cpData As Integer) As Integer 
   Private Declare Function RegCloseKey Lib "advapi32.dll" _
                  (hKey As IntPtr) As Integer

   ' Windows API constants.
   Private Const HKEY_CLASSES_ROOT As Integer = &h80000000
   Private Const ERROR_SUCCESS As Integer = 0

   Private Const KEY_QUERY_VALUE As Integer = 1
   Private Const KEY_SET_VALUE As Integer = &h2
   
   Private REG_SZ As UInteger = 1
   
   Private Const MAX_PATH As Integer  = 260
   
   Public Sub New(fileExtension As String)
      Dim retVal As Integer = 0
      Dim lpType As UInteger = 0
                  
      If Not fileExtension.StartsWith(".") Then 
         fileExtension = "." + fileExtension
      End If   
      ext = fileExtension
       
      Dim hExtension As IntPtr = IntPtr.Zero
      ' Get the file extension value.
      retVal = RegOpenKeyEx(New IntPtr(HKEY_CLASSES_ROOT), fileExtension, 0, 
                            KEY_QUERY_VALUE, hExtension)
      if retVal <> ERROR_SUCCESS Then 
         Throw New Win32Exception(retVal)
      End If  
      ' Instantiate the first SafeRegistryHandle.
      hExtHandle = New SafeRegistryHandle(hExtension, True)
      
      Dim appId As New String(" "c, MAX_PATH)
      Dim appIdLength As UInteger = CUInt(appId.Length)
      retVal = RegQueryValueEx(hExtHandle.DangerousGetHandle(), String.Empty, _
                               0, lpType, appId, appIdLength)
      if retVal <> ERROR_SUCCESS Then
         Throw New Win32Exception(retVal)
      End If   
      ' We no longer need the hExtension handle.
      hExtHandle.Dispose()

      ' Determine the number of characters without the terminating null.
      appId = appId.Substring(0, CInt(appIdLength) \ 2 - 1) + "\shell\open\Command"

      ' Open the application identifier key.
      Dim exeName As New string(" "c, MAX_PATH)
      Dim exeNameLength As UInteger = CUInt(exeName.Length)
      Dim hAppId As IntPtr
      retVal = RegOpenKeyEx(New IntPtr(HKEY_CLASSES_ROOT), appId, 0, 
                            KEY_QUERY_VALUE Or KEY_SET_VALUE, hAppId)
      If retVal <> ERROR_SUCCESS Then 
         Throw New Win32Exception(retVal)
      End If   

      ' Instantiate the second SafeRegistryHandle.
      hAppIdHandle = New SafeRegistryHandle(hAppId, True)

      ' Get the executable name for this file type.
      Dim exePath As New string(" "c, MAX_PATH)
      Dim exePathLength As UInteger = CUInt(exePath.Length)
      retVal = RegQueryValueEx(hAppIdHandle.DangerousGetHandle(), _
                               String.Empty, 0, lpType, exePath, exePathLength)
      If retVal <> ERROR_SUCCESS Then
         Throw New Win32Exception(retVal)
      End If     
      ' Determine the number of characters without the terminating null.
      exePath = exePath.Substring(0, CInt(exePathLength) \ 2 - 1)
  
      exePath = Environment.ExpandEnvironmentVariables(exePath)
      Dim position As Integer = exePath.IndexOf("%"c)
      If position >= 0 Then
         args = exePath.Substring(position)
         ' Remove command line parameters ('%0', etc.).
         exePath = exePath.Substring(0, position).Trim()
      End If   
      openCmd = exePath
   End Sub

   Public ReadOnly Property Extension As String
      Get
         Return ext
      End Get
   End Property
   
   Public Property Open As String
      Get
         Return openCmd
      End Get    
      Set 
        If hAppIdHandle.IsInvalid Or hAppIdHandle.IsClosed Then
           Throw New InvalidOperationException("Cannot write to registry key.")
        End If    
        If Not File.Exists(value) Then
           Dim message As String = String.Format("'{0}' does not exist", value)
           Throw New FileNotFoundException(message) 
        End If
        Dim cmd As String = value + " %1"
        Dim retVal As Integer = RegSetValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0, 
                                              REG_SZ, value, value.Length + 1)
        If retVal <> ERROR_SUCCESS Then 
           Throw New Win32Exception(retVal)
        End If                             
      End Set
   End Property   
   
   Public Sub Dispose() _
      Implements IDisposable.Dispose 
      Dispose(true)
      GC.SuppressFinalize(Me)
   End Sub   
   
   Protected Sub Dispose(disposing As Boolean)
      ' Ordinarily, we release unmanaged resources here 
      ' but all are wrapped by safe handles.
      
      ' Release disposable objects.
      If disposing Then
         If hExtHandle IsNot Nothing Then hExtHandle.Dispose()
         If hAppIdHandle IsNot Nothing Then hAppIdHandle.Dispose()
      End If
   End Sub
End Class

적용 대상

추가 정보