El Registro de Windows es una base de datos jerárquica de configuración que Windows y el software instalado en él leen y escriben en tiempo de ejecución. En C# accedemos a él mediante dos tipos del espacio de nombres Microsoft.Win32: Registry para un único valor y RegistryKey para todo lo demás.

Registry.GetValue() y Registry.SetValue() reciben la ruta completa de una clave y hacen una sola lectura o una sola escritura. RegistryKey representa una clave abierta, así que puede enumerar, eliminar, consultar los tipos de los valores y establecer permisos, y hay que liberarla.

El Registro solo existe en Windows, así que cada llamada lleva una protección. OperatingSystem.IsWindows() es esa protección, y sin ella el compilador emite CA1416 en código que lanzaría una excepción en cualquier otra plataforma.

Para descargar el código fuente de este artículo, puedes visitar nuestro repositorio de GitHub.

¿Qué es el Registro de Windows?

El Registro de Windows es una base de datos jerárquica que Windows, sus controladores de dispositivo y las aplicaciones instaladas usan para guardar la configuración como pares nombre-valor.

Tiene forma de árbol. Cada nodo de ese árbol es una clave, y una clave contiene subclaves y también valores, cada uno formado por un nombre asociado a unos datos y a un tipo declarado. La comparación más fácil de captar es la de un sistema de archivos: las claves se comportan como directorios y los valores, como los archivos que hay dentro de ellos.

El árbol tiene un conjunto fijo de raíces. Windows las mantiene abiertas por nosotros, y se llaman claves predefinidas, claves raíz o claves base.

Dos de esas raíces hacen casi todo el trabajo. HKEY_CURRENT_USER guarda la configuración de la cuenta con la que se ejecuta nuestro proceso. HKEY_LOCAL_MACHINE guarda la configuración que comparten todas las cuentas de la máquina.

Esa división es lo que importa en la práctica. De forma predeterminada, escribir dentro de HKEY_CURRENT_USER no requiere permisos especiales, mientras que escribir dentro de HKEY_LOCAL_MACHINE requiere un proceso con privilegios elevados. Es lo primero que falla.

Cuando esas escrituras fallan, la solución es ejecutar la aplicación como administrador.

Estructura del Registro de Windows

El Registro de Windows organiza sus datos en una estructura de árbol. Cada nodo de ese árbol es una clave, y la referencia de Microsoft sobre el Registro de Win32 explica el resto sin rodeos: “Cada clave puede contener tanto subclaves como entradas de datos llamadas valores”. Además, podemos considerar el Registro de Windows como una base de datos de pares nombre-valor. Otra forma de ver el Registro de Windows es como una estructura de directorios. Básicamente, la clave representa el directorio, y el contenido del directorio son subdirectorios/subclaves o archivos/pares nombre-valor.

El Registro de Windows incluye un conjunto de claves predefinidas. Una aplicación puede usar identificadores (handles) para acceder a esas claves. Las claves predefinidas están siempre abiertas y sirven como puntos de entrada al Registro. Cada versión de Windows puede tener un conjunto distinto de claves predefinidas. A esas claves también las llamamos elementos raíz, claves raíz o claves base. Estas son las que expone la clase Registry:

Clave baseCampo de RegistryMiembro de RegistryHiveQué contiene
HKEY_CLASSES_ROOTRegistry.ClassesRootRegistryHive.ClassesRootAsociaciones de extensiones de nombre de archivo y datos de registro de clases COM.
HKEY_CURRENT_USERRegistry.CurrentUserRegistryHive.CurrentUserConfiguración de la cuenta con la que se ejecuta nuestro proceso. De forma predeterminada, se puede escribir sin privilegios elevados.
HKEY_LOCAL_MACHINERegistry.LocalMachineRegistryHive.LocalMachineConfiguración de toda la máquina. De forma predeterminada, escribir requiere un proceso con privilegios elevados.
HKEY_USERSRegistry.UsersRegistryHive.UsersLos perfiles de usuario cargados actualmente en la máquina.
HKEY_CURRENT_CONFIGRegistry.CurrentConfigRegistryHive.CurrentConfigDiferencias entre el perfil de hardware actual y el estándar.
HKEY_PERFORMANCE_DATARegistry.PerformanceDataRegistryHive.PerformanceDataDatos de los contadores de rendimiento, que se generan bajo demanda en lugar de guardarse en disco.

Win32 define algunas otras claves predefinidas que la clase Registry no expone, entre ellas HKEY_CURRENT_USER_LOCAL_SETTINGS, y aun así podemos llegar a ellas pasando su identificador predefinido, envuelto en un SafeRegistryHandle, a RegistryKey.FromHandle().

Para saber más, consulta la página sobre las claves predefinidas.

Valores de las claves del Registro de Windows

Los valores de las claves del Registro de Windows pueden ser de distintos tipos:

Tipo del RegistroMiembro de RegistryValueKindGetValue() lo devuelve comoNotas
REG_SZStringstringUna cadena terminada en null. Es el tipo predeterminado de SetValue() con un string.
REG_EXPAND_SZExpandStringstringContiene referencias sin expandir, como %PATH%. GetValue() las expande salvo que pasemos RegistryValueOptions.DoNotExpandEnvironmentNames.
REG_BINARYBinarybyte[]Cualquier dato en formato binario.
REG_DWORDDWordintUn número de 32 bits. REG_DWORD_LITTLE_ENDIAN es el mismo tipo con otro nombre.
REG_QWORDQWordlongUn número de 64 bits.
REG_MULTI_SZMultiStringstring[]Un array de cadenas terminadas en null.
REG_NONENonebyte[]El tipo del valor no está definido.
REG_DWORD_BIG_ENDIAN, REG_LINK, REG_RESOURCE_LISTUnknownbyte[] para REG_DWORD_BIG_ENDIAN, el valor predeterminado que pasamos para los otros dosRegistryValueKind no tiene ningún miembro para estos tipos, así que GetValueKind() indica Unknown.

Cuando el sistema operativo arranca o un nuevo usuario inicia sesión, se carga en memoria el nuevo conjunto de claves del Registro. Este conjunto depende del perfil de usuario, de la configuración y de las preferencias. A un conjunto así se le llama subárbol (hive). Un subárbol no es algo que una aplicación cree al añadir una clave.

Abrir el Registro de Windows

¡AVISO! Vamos a mostrar cómo abrir y examinar el Registro de Windows. Más adelante, también haremos algunos cambios en el Registro de Windows desde código. El lector asume toda la responsabilidad por cualquier cambio involuntario en su sistema y por los posibles problemas causados al modificar o eliminar claves del Registro manualmente o al ejecutar la aplicación de demostración. Se recomienda encarecidamente crear una copia de seguridad del Registro de Windows antes de hacer cualquier cambio.

Podemos abrir el Registro de Windows de dos formas:

  • Escribe regedit en el cuadro de búsqueda de la barra de tareas y selecciona la aplicación Registry Editor (Editor del Registro).
  • Haz clic con el botón derecho en el botón Inicio de Windows, selecciona Run (Ejecutar), escribe regedit en el cuadro y pulsa OK.

En la máquina de cada usuario, el Registro de Windows puede verse algo distinto, pero la siguiente imagen muestra su estructura básica:

Estructura del Registro de Windows

HKEY_CLASSES_ROOT es una clave base, una vista combinada de las claves Classes que hay en HKEY_LOCAL_MACHINE y HKEY_CURRENT_USER, no un subárbol.

¿Cómo acceder al Registro de Windows en .NET?

Casi todo lo que necesitamos está en el espacio de nombres Microsoft.Win32, que viene incluido en la biblioteca de clases base. No hay que instalar ningún paquete NuGet ni añadir ninguna referencia.

Toda la API se reparte entre dos tipos. Registry es una clase estática que expone las seis claves base como campos y añade exactamente dos métodos, GetValue() y SetValue(), que reciben cada uno la ruta completa de una clave.

RegistryKey es una clase no estática que representa una clave abierta, y reúne todo lo demás: abrir, crear, eliminar, enumerar, leer los tipos de los valores y controlar el acceso. Implementa IDisposable porque cada instancia posee un identificador abierto a una clave nativa.

El Registro es una característica de Windows, así que toda la API está anotada como exclusiva de Windows. Con un framework de destino que no sea específico de Windows, el compilador emite CA1416 en cada punto de llamada del que no puede demostrar que se ejecuta en Windows.

Proteger la llamada con OperatingSystem.IsWindows() satisface al analizador y mantiene multiplataforma el resto de la aplicación.

Las clases Registry y RegistryKey son las principales para interactuar con el Registro de Windows.

La clase Registry

La clase Registry da acceso a las claves raíz. Además, implementa métodos estáticos para leer o escribir pares clave-valor. Todos los métodos y constantes de este artículo van dentro de una public static class RegistryDemo en su propio archivo, que empieza con using Microsoft.Win32;. Por ejemplo, podemos imprimir el nombre de HKEY_CURRENT_USER. A esta primera versión le falta a propósito la comprobación de plataforma, para que veamos qué dice el compilador:

public static string GetCurrentUserRootKeyName()
{
    var registryKey = Registry.CurrentUser;
    return registryKey.Name;
}

En el método anterior, vemos advertencias del compilador:

warning CA1416: This call site is reachable on all platforms. 'RegistryKey.Name' is only supported on: 'windows'.
warning CA1416: This call site is reachable on all platforms. 'Registry.CurrentUser' is only supported on: 'windows'.

El Registro de Windows es una característica específica de Windows que no existe en otros sistemas operativos.

The Web API Production Checklist, ebook gratuito en inglés

Ebook gratis

¿Tu Web API está lista para producción?

33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.

El ebook está en inglés.

Descarga la checklist gratis

PDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.

Sin embargo, .NET es un framework multiplataforma. Por eso, el compilador nos advierte de que este código no se ejecutará fuera de un entorno Windows. Es una buena práctica comprobar en qué sistema operativo se ejecuta nuestro código y ejecutar esta parte del código en consecuencia:

public static string GetCurrentUserRootKeyNameWithPlatformCheck()
{
    if (!OperatingSystem.IsWindows())
    {
        return "Unsupported platform";
    }

    return Registry.CurrentUser.Name;
}

La clase RegistryKey

La clase RegistryKey encapsula el nodo correspondiente a una clave del Registro de Windows. Ofrece los métodos para trabajar con la clave y sus pares nombre-valor, consultar ciertas propiedades de la clave del Registro y obtener o establecer el control de acceso sobre ella. Por ejemplo, podemos contar el número de subclaves de una clave concreta:

public static int GetCurrentUserRootKeySubkeyCount()
{
    if (!OperatingSystem.IsWindows())
    {
        return -1;
    }

    return Registry.CurrentUser.SubKeyCount;
}

En las secciones siguientes veremos otros métodos y propiedades de la clase RegistryKey.

Casos de uso habituales del Registro de Windows

Entre los casos de uso más habituales del Registro de Windows están los siguientes:

  • Configuración del sistema: configuración y comportamiento del propio Windows
  • Configuración del hardware: controladores de dispositivo y ajustes del hardware
  • Configuración del software: preferencias, información de licencias, tamaño y posición de las ventanas y otros datos específicos de cada aplicación
  • Perfiles de usuario: configuraciones y ajustes específicos de cada usuario que permiten a los usuarios tener interfaces personalizadas
  • Configuración de seguridad: directivas de seguridad y de grupo, configuración de las cuentas de usuario
  • Asociaciones de archivos: ajustes específicos de cada tipo de archivo que permiten a las aplicaciones abrir archivos con determinadas extensiones
  • Configuración de inicio: información sobre los programas que deben iniciarse automáticamente con Windows

Las preferencias de las aplicaciones son el único elemento de esa lista con una alternativa portable: el sistema de configuración de .NET lee esos mismos ajustes sin atar la aplicación a Windows. El Registro sigue teniendo su razón de ser cuando el ajuste pertenece a la máquina y no a un despliegue concreto, como suele ocurrir con una aplicación que se ejecuta como servicio de Windows.

¿Cómo escribir y leer un valor del Registro en C#?

Escribir un valor requiere una sola llamada. Registry.SetValue() recibe la ruta completa de la clave, el nombre del valor y los datos, y crea la clave si todavía no existe.

Para volver a leerlo basta con la llamada equivalente. Registry.GetValue() recibe la misma ruta, el mismo nombre de valor y un valor predeterminado que devuelve cuando ese nombre no existe. Devuelve el resultado como object, así que tenemos que convertirlo nosotros.

El segundo parámetro es el que se presta a confusión. Nombra un valor dentro de la clave, no otro tramo de la ruta de la clave. En nuestro ejemplo, HKEY_CURRENT_USER\CodeMazeRegistryDemoSubKey es la clave y CodeMazeRegistryDemoName es un valor que hay dentro de ella.

Hay dos búsquedas fallidas distintas que se parecen. GetValue() devuelve null cuando no encuentra la clave en absoluto, y devuelve el valor predeterminado que le pasamos cuando la clave existe pero no tiene ningún valor con ese nombre.

Además, cada clave puede tener un valor predeterminado sin nombre, que se lee y se escribe pasando null o una cadena vacía como nombre.

En los siguientes fragmentos de código usaremos algunas constantes:

private const string CodeMazeRegistryDemoSubKey = "CodeMazeRegistryDemoSubKey";
private const string CodeMazeRegistryDemoName = "CodeMazeRegistryDemoName";
public const string CodeMazeRegistryDemoValue = "CodeMazeRegistryDemoValue";

CodeMazeRegistryDemoSubKey es el nombre de la subclave con la que trabajaremos y CodeMazeRegistryDemoName es el nombre del valor dentro de la clave. Por último, CodeMazeRegistryDemoValue es el valor que escribiremos en el par nombre-valor. Como ejemplo, usaremos la clave base Registry.CurrentUser (HKEY_CURRENT_USER).

Leer y escribir en el Registro de Windows con la clase Registry

La clase Registry implementa los métodos estáticos GetValue() y SetValue(). Veamos cómo usarlos:

public static string ReadAndWriteRegistryValueUsingRegistryClass()
{
    if (!OperatingSystem.IsWindows())
    {
        return string.Empty;
    }

    var subKeyToWrite = $@"{Registry.CurrentUser.Name}\{CodeMazeRegistryDemoSubKey}";

    Registry.SetValue(subKeyToWrite, CodeMazeRegistryDemoName, CodeMazeRegistryDemoValue);
    var writtenValue = Registry.GetValue(subKeyToWrite, CodeMazeRegistryDemoName, string.Empty);

    Registry.CurrentUser.DeleteSubKey(CodeMazeRegistryDemoSubKey);

    return writtenValue?.ToString() ?? string.Empty;
}

El método SetValue() tiene tres parámetros: la ruta completa de la subclave, el nombre y el valor que escribirá en el Registro. Si la subclave no existe, se crea. Si la clave no existe, se crea. En caso contrario, el método escribe el nombre y el valor en la clave indicada.

El método SetValue() tiene una sobrecarga adicional que permite indicar el tipo de datos del valor. El método abre y cierra la clave automáticamente al escribir el valor en ella.

Igual que el método SetValue(), GetValue() también tiene tres parámetros: la ruta completa de la subclave, el nombre y el valor predeterminado que se devolverá si el nombre no existe. El método devuelve null si no encuentra la subclave y el valor predeterminado si el nombre no existe. El valor devuelto es de tipo object, y tenemos que hacer una conversión explícita o convertirlo al tipo adecuado.

Cada clave del Registro puede contener un valor predeterminado sin nombre, que el método GetValue() devuelve si pasamos null o string.Empty como nombre. En el método SetValue() podemos usar null o string.Empty como argumento del nombre si queremos guardar el valor con el nombre predeterminado.

Default value for Windows Registry Key

Nuestro código escribe el valor llamado CodeMazeRegistryDemoName; la captura de pantalla muestra un nombre distinto.

Por último, llamamos al método DeleteSubKey() de la clase RegistryKey para eliminar la clave que hemos creado:

Registry.CurrentUser.DeleteSubKey(CodeMazeRegistryDemoSubKey);

Leer y escribir en el Registro de Windows con la clase RegistryKey

Como ya hemos mencionado, la clase RegistryKey representa un nodo del Registro de Windows. Implementa varios métodos para trabajar con las claves del Registro:

public static string ReadAndWriteRegistryValueUsingRegistryKeyClass()
{
    if (!OperatingSystem.IsWindows())
    {
        return string.Empty;
    }

    var baseKey = Registry.CurrentUser;

    using var subKey = baseKey.OpenSubKey(CodeMazeRegistryDemoSubKey, true) ??
                       baseKey.CreateSubKey(CodeMazeRegistryDemoSubKey);

    subKey.SetValue(CodeMazeRegistryDemoName, CodeMazeRegistryDemoValue);
    var writtenValue = subKey.GetValue(CodeMazeRegistryDemoName);
    subKey.DeleteValue(CodeMazeRegistryDemoName);

    baseKey.DeleteSubKey(CodeMazeRegistryDemoSubKey);

    return writtenValue?.ToString() ?? string.Empty;
}

Abrimos la subclave con el método OpenSubKey(). Este método tiene cinco sobrecargas, que gestionan el control de acceso de distintas formas. En nuestro ejemplo, usamos la sobrecarga OpenSubKey(string, bool), con la que podemos abrir la subclave con acceso de escritura. Si omitimos este parámetro, la clave se abre solo con acceso de lectura y el método SetValue() lanza una excepción. Si la clave indicada no existe, el método devuelve null.

OpenSubKey(name) abre la clave en modo de solo lectura, así que cualquier escritura a través de ella lanza una excepción UnauthorizedAccessException; OpenSubKey(name, true) y CreateSubKey(name) son dos formas de obtener una clave en la que podemos escribir.

En nuestro caso, la clave siempre será null, porque la eliminamos con el método DeleteSubKey() al final del método ReadAndWriteRegistryValueUsingRegistryKeyClass(). En cambio, si comentamos la llamada al método DeleteSubKey() y volvemos a ejecutar la aplicación, la clave existirá.

En la línea siguiente, creamos la clave con una llamada al método CreateSubKey(). Este método abre la clave con acceso de escritura. Tiene siete sobrecargas que nos permiten crear la clave con distintos controles de acceso y opciones.

Con la llamada al método SetValue() de la clase RegistryKey, escribimos el par nombre-valor en la clave. Si el nombre ya existe, el valor se actualiza. Los parámetros del método son el nombre y el valor, porque la ruta de la clave del Registro ya está encapsulada en la variable subKey.

El método GetValue() lee el valor con el nombre indicado. Además, a continuación llamamos al método DeleteValue(), que elimina el valor con el nombre indicado. Sin embargo, este paso no es técnicamente necesario, porque después eliminamos la subclave entera llamando al método DeleteSubKey().

Por último, devolvemos el valor como cadena. Conviene tener en cuenta que la clase RegistryKey también tiene un método ToString() que devuelve el nombre de la clave como cadena.

The Web API Production Checklist, ebook gratuito en inglés

Ebook gratis

¿Tu Web API está lista para producción?

33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.

El ebook está en inglés.

Descarga la checklist gratis

PDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.

Liberar RegistryKey

La clase RegistryKey implementa la interfaz IDisposable, así que todo lo que ya sabemos sobre cómo gestionar objetos IDisposable se aplica también aquí. En nuestro caso, la envolvemos en una declaración using, lo que implica que el método Dispose() se llamará automáticamente.

Como alternativa, si no usamos la declaración using, podemos llamar directamente al método Dispose(), que en consecuencia cierra la clave, igual que una llamada directa al método Close(). La llamada al método Close() libera el identificador de la clave, y ya no es posible escribir nada más.

Del mismo modo, podemos llamar al método Flush() si queremos asegurarnos de que los cambios de la clave se escriben en el disco, pero en los casos habituales no hace falta, porque los cambios en el Registro se guardan automáticamente.

Registry o RegistryKey: ¿qué clase conviene usar?

Las dos clases leen y escriben valores. La diferencia está en cuántas veces accedemos a la clave.

Registry sirve para accesos puntuales. Cada llamada a GetValue() o SetValue() abre la clave, hace el trabajo y vuelve a cerrarla, así que leer un solo ajuste ocupa una línea y no deja nada que liberar.

RegistryKey sirve para todo lo demás. Abrimos o creamos la clave una vez, conservamos el identificador y llamamos sobre ella a tantos métodos como necesitemos. Es también la única vía para la enumeración, la lectura de los tipos de los valores, la eliminación, el control de acceso, las vistas de 32 y 64 bits y las máquinas remotas.

Ese identificador es la razón por la que la elección importa más allá de la comodidad. RegistryKey implementa IDisposable, así que su lugar está en una declaración using, y Registry no tiene nada que liberar.

Una regla resuelve la mayoría de los casos. Para escribir a través de RegistryKey hace falta una clave abierta para escritura, así que llamamos a OpenSubKey(name, true) o a CreateSubKey(name). OpenSubKey(name) con un solo argumento abre la clave en modo de solo lectura, y SetValue() sobre ella lanza una excepción.

OperaciónRegistry (estática)RegistryKey (de instancia)
Leer un valorRegistry.GetValue(path, name, default)key.GetValue(name)
Escribir un valorRegistry.SetValue(path, name, value)key.SetValue(name, value)
Abrir una clave existenteno disponiblekey.OpenSubKey(name) o OpenSubKey(name, writable)
Crear una claveocurre implícitamente dentro de SetValue()key.CreateSubKey(name)
Eliminar un valorno disponiblekey.DeleteValue(name)
Eliminar una claveno disponiblekey.DeleteSubKey(name), key.DeleteSubKeyTree(name)
Listar subclaves o nombres de valoresno disponiblekey.GetSubKeyNames(), key.GetValueNames()
Leer el tipo de un valorno disponiblekey.GetValueKind(name)
Leer o establecer el control de accesono disponiblekey.GetAccessControl(), key.SetAccessControl()
Elegir la vista de 32 o de 64 bitsno disponibleRegistryKey.OpenBaseKey(hive, view)
Acceder a otra máquinano disponibleRegistryKey.OpenRemoteBaseKey(hive, machine)
Hay que liberarlanosí, implementa IDisposable

La misma regla se aplica al resto de las operaciones de escritura: CreateSubKey(), DeleteSubKey(), DeleteSubKeyTree() y DeleteValue() pasan por la misma comprobación de escritura que SetValue().

¿Qué más se puede hacer con el Registro de Windows en C#?

La clase RegistryKey implementa métodos para trabajar de forma más avanzada con el Registro de Windows. Veamos algunos.

Enumerar claves y valores

Podemos obtener un array con todas las subclaves de una clave concreta llamando al método GetSubKeyNames():

public static string[] GetSubKeyNames()
{
    if (!OperatingSystem.IsWindows())
    {
        return [];
    }

    using var subKey = Registry.CurrentUser.CreateSubKey(CodeMazeRegistryDemoSubKey);
    subKey.CreateSubKey("SubKey1");
    subKey.CreateSubKey("SubKey2");

    var subKeyNames = subKey.GetSubKeyNames();

    Registry.CurrentUser.DeleteSubKeyTree(CodeMazeRegistryDemoSubKey);

    return subKeyNames;
}

Aparte de esto, podemos obtener un array con todos los nombres de valores llamando al método GetValueNames():

public static string[] GetValueNames()
{
    if (!OperatingSystem.IsWindows())
    {
        return [];
    }

    using var subKey = Registry.CurrentUser.CreateSubKey(CodeMazeRegistryDemoSubKey);
    using var subKey1 = subKey.CreateSubKey("SubKey1");
    subKey1.SetValue("Name1", "Value1");
    subKey1.SetValue("Name2", "Value2");

    var subKeyNames = subKey1.GetValueNames();

    Registry.CurrentUser.DeleteSubKeyTree(CodeMazeRegistryDemoSubKey);

    return subKeyNames;
}

CreateSubKey() devuelve una clave, nunca null, así que en ninguno de los dos métodos hay operador condicional null ni array de respaldo.

Obtener el tipo de datos del valor de una clave del Registro

La clase RegistryKey implementa el método GetValueKind(). Devuelve el tipo de datos del valor con el nombre indicado:

public static string GetValueKind()
{
    if (!OperatingSystem.IsWindows())
    {
        return string.Empty;
    }

    using var subKey = Registry.CurrentUser.CreateSubKey(CodeMazeRegistryDemoSubKey);
    using var subKey1 = subKey.CreateSubKey("SubKey1");
    subKey1.SetValue("Name1", "Value1");

    var valueKind = subKey1.GetValueKind("Name1");

    Registry.CurrentUser.DeleteSubKeyTree(CodeMazeRegistryDemoSubKey);

    return valueKind.ToString();
}

El método GetValueKind() devuelve un miembro de la enumeración RegistryValueKind, definida en el espacio de nombres Microsoft.Win32.

Permisos de acceso a las claves del Registro

Los métodos CreateSubKey() y OpenSubKey() ofrecen varias sobrecargas que aceptan parámetros para controlar con precisión los derechos de acceso a la clave del Registro.

Con la enumeración RegistryKeyPermissionCheck podemos aplicar las comprobaciones de seguridad al abrir la clave y al acceder a sus valores. Además, con la enumeración RegistryRights podemos lograr un control de acceso detallado sobre la clave del Registro, una vez que añadamos using System.Security.AccessControl; y using System.Security.Principal; al principio del archivo:

public static bool SetRegistryKeyAccessPermissions()
{
    if (!OperatingSystem.IsWindows())
    {
        return false;
    }

    var user = $@"{Environment.UserDomainName}\{Environment.UserName}";
    var registrySecurity = new RegistrySecurity();

    var accessRule = new RegistryAccessRule(user,
        RegistryRights.ReadKey | RegistryRights.WriteKey | RegistryRights.Delete,
        InheritanceFlags.None,
        PropagationFlags.None,
        AccessControlType.Allow);

    registrySecurity.AddAccessRule(accessRule);

    using var subKey = Registry.CurrentUser.CreateSubKey(CodeMazeRegistryDemoSubKey,
        RegistryKeyPermissionCheck.Default, registrySecurity);

    if (subKey == null)
    {
        return false;
    }

    var isAdded = false;
    var accessControl = subKey.GetAccessControl();
    var accessRules = accessControl.GetAccessRules(true, true, typeof(NTAccount));
    foreach (RegistryAccessRule rule in accessRules)
    {
        if (rule.IdentityReference.Value == user)
        {
            isAdded = true;
            break;
        }
    }
    Registry.CurrentUser.DeleteSubKeyTree(CodeMazeRegistryDemoSubKey);

    return isAdded;
}

En nuestro ejemplo, creamos la subclave pasando los argumentos de comprobación de permisos y de seguridad del Registro. La comprobación de permisos usa el valor predeterminado, lo que significa que la comprobación de seguridad se hereda de la clave padre. Además de la comprobación de permisos, la clase RegistrySecurity nos permite añadir reglas de acceso al Registro. Como ejemplo, definimos que el usuario puede leer, escribir y eliminar la clave indicando los valores de RegistryRights deseados. RegistryRights es una enumeración de indicadores, así que los valores se combinan con el operador OR bit a bit (|).

El método GetAccessControl() devuelve la definición de seguridad del Registro para la clave y, a través de ella, podemos enumerar todas las reglas definidas. Después, comprobamos si nuestra regla está aplicada a la clave.

Con el método relacionado SetAccessControl(), podemos establecer o modificar la definición de seguridad de la clave (siempre que tengamos el acceso de seguridad necesario para cambiar los derechos de seguridad).

Leer y escribir en un Registro de Windows remoto

La clase RegistryKey implementa el método estático OpenRemoteBaseKey(). Con él, abrimos la clave base en una máquina a la que tenemos una conexión de red remota:

public static bool OpenRemoteBaseKey(string machineName)
{
    if (!OperatingSystem.IsWindows())
    {
        return false;
    }

    try
    {
        using var remoteBaseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.CurrentUser, machineName);

        return true;
    }
    catch (ArgumentException)
    {
        // A null machine name surfaces here.
        return false;
    }
    catch (IOException)
    {
        return false;
    }
}

La clave devuelta es un identificador abierto como cualquier otro, así que la envolvemos en una declaración using. Los bloques catch son específicos a propósito: una máquina inaccesible llega como una excepción IOException y no como una excepción de red o de seguridad, algo fácil de malinterpretar desde fuera.

Para acceder al Registro remoto, el servicio de Windows Remote Registry debe estar habilitado en la máquina remota y, por supuesto, debemos tener derechos de administrador en ella.

Conclusión

El Registro de Windows es un lugar central y estructurado en el que las aplicaciones, el sistema operativo y el hardware guardan distintos tipos de valores necesarios para su correcto funcionamiento. Por eso, representa una evolución muy bienvenida de los archivos .INI y ofrece capacidades y características mucho más ricas.

El Registro de Windows es específico del sistema operativo Windows y, por lo tanto, debemos comprobar en el código que existe y que podemos acceder a él.

Las clases Registry y RegistryKey ofrecen acceso desde código para leer y escribir entradas del Registro de Windows. También permiten operaciones más avanzadas, como el control detallado de los derechos de acceso, además del acceso al Registro de máquinas remotas.

Probado con el SDK de .NET 10.0.302.