Object.Finalize Object.Finalize Object.Finalize Object.Finalize Method

정의

가비지 컬렉션이 회수하기 전에 개체가 리소스를 해제하고 다른 정리 작업을 수행할 수 있게 합니다. 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 메서드를 가비지 수집기에서 파생 된 형식으로 표시 되지 않습니다 Object 종료에 대 한 재정의 하지 않는 한를 Finalize 메서드.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.

Finalize 자동으로 한 번만 호출 인스턴스에, 개체는 같은 메커니즘을 사용 하 여 다시 등록 하지 않는 한 GC.ReRegisterForFinalize 하며 GC.SuppressFinalize 메서드가 이후에 호출 되지 않습니다.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 개체는 사용할 수 있는 관리 되지 않는 리소스를 래핑하는 권장 되는 대신 safehandle 사용 하 여 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. 자세한 내용은 The 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.

Visual 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. 리소스를 해제 하려면 즉시 수도 있습니다를 구현 하는 삭제 패턴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.

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. 그러나 개체 수만 복원할 되지 않습니다. Finalize 가비지 수집 중 부활 개체에서 호출할 수 없습니다.However, the object can only be resurrected once; Finalize cannot be called on resurrected objects during garbage collection. 하나의 액션이 구현의 Finalize 해서는 안 됩니다: 예외를 throw 하지 해야 합니다.There is one action that your implementation of Finalize should never take: it should never throw an exception. 메서드에서 throw 된 예외에서 호출 된 경우는 Finalize 메서드는 처리 되지를 Finalize 메서드를 런타임에 가정를 Finalize 메서드는 반환 하 고 계속 호출 하는 Finalize 다른 개체의 메서드.If any exceptions thrown by methods called from the Finalize method are unhandled by the Finalize method, the runtime assumes that the Finalize method returned and continues to call the Finalize methods of other objects.

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의 클래스를 제공 합니다 Microsoft.Win32 에서 파생 되는 네임 스페이스 System.Runtime.InteropServices.SafeHandle:The .NET Framework provides the following classes in the Microsoft.Win32 namespace that are derived from System.Runtime.InteropServices.SafeHandle:

다음 예제에서는 합니다 삭제 패턴 재정의 하는 대신 safehandle을 사용 하 여는 Finalize 메서드.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. 로 반환 하는 두 레지스트리 핸들 out Windows에서 매개 변수 RegOpenKeyEx 함수 호출에 전달 되는 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
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

적용 대상

추가 정보