C# Registry ObjectDisposedException Cases

I'm writing an application that accesses the Windows registry in C#.

My question on a high level is: Are there any circumstances that would cause an ObjectDisposedException be thrown when using Registry.LocalMachine.OpenSubKey(key)?

This code would only be run under Administrative privileges in my application and the key is a constant that will never be null. My first instinct would be that an ObjectDisposedException would never be thrown by this static call because the MSDN document (linked here) states that it would only be thrown when the RegistryKey is closed but I can't find in the MSDN whether or not it is possible to ever run into a closed RegistryKey through the static call.

Thanks for any help!

1 answer

  • answered 2018-01-11 21:13 Scott Hannen

    The test is this:

    Registry.LocalMachine.Close();
    Registry.LocalMachine.Dispose();
    var hardwareKey = Registry.LocalMachine.OpenSubKey("HARDWARE");
    

    Registry.LocalMachine returns a RegistryKey. If you can actually close and/or dispose it then the following line should throw an exception, which it won't.

    If you do the same thing with another registry key - close it and then try to open a subkey, it will throw an ObjectDisposedException.

    var hardwareKey = Registry.LocalMachine.OpenSubKey("HARDWARE");
    hardwareKey.Close();
    // throws the exception.
    var descriptionKey = hardwareKey.OpenSubKey("DESCRIPTION");
    

    Why do they behave differently?

    According to the source code, RegistryKey.Close calls Dispose. But the particular RegistryKey returned by Registry.LocalMachine will never be closed.

    This isn't documented (other than the source code) so it's up to you whether you want to rely on it. But it makes sense that if you're accessing the registry key via a static method then you shouldn't be able to close/dispose it.

    If you follow the logic, system keys (including HKEY_LOCAL_MACHINE) will not be disposed except for HKEY_PERFORMANCE_DATA.

    [System.Security.SecuritySafeCritical]  // auto-generated
    private void Dispose(bool disposing)
    {
        if (hkey != null)
        {
            if (!IsSystemKey())
            {
                try
                {
                    hkey.Dispose();
                }
                catch (IOException)
                {
                    // we don't really care if the handle is invalid at this point
                }
                finally
                {
                    hkey = null;
                }
            }
            else if (disposing && IsPerfDataKey())
            {
                // System keys should never be closed.  However, we want to call RegCloseKey
                // on HKEY_PERFORMANCE_DATA when called from PerformanceCounter.CloseSharedResources
                // (i.e. when disposing is true) so that we release the PERFLIB cache and cause it
                // to be refreshed (by re-reading the registry) when accessed subsequently. 
                // This is the only way we can see the just installed perf counter.  
                // NOTE: since HKEY_PERFORMANCE_DATA is process wide, there is inherent ---- in closing
                // the key asynchronously. While Vista is smart enough to rebuild the PERFLIB resources
                // in this situation the down level OSes are not. We have a small window of ---- between  
                // the dispose below and usage elsewhere (other threads). This is By Design. 
                // This is less of an issue when OS > NT5 (i.e Vista & higher), we can close the perfkey  
                // (to release & refresh PERFLIB resources) and the OS will rebuild PERFLIB as necessary. 
                SafeRegistryHandle.RegCloseKey(RegistryKey.HKEY_PERFORMANCE_DATA);
            }
        }
    }