Lectura del registro de 64 bits desde una aplicación de 32 bits


Tengo un proyecto de prueba de unidad c# que está compilado para AnyCPU. Nuestro servidor de compilación es una máquina de 64 bits y tiene instalada una instancia de SQL Express de 64 bits.

El proyecto de prueba utiliza un código similar al siguiente para identificar la ruta al .Archivos MDF:

    private string GetExpressPath()
    {
        RegistryKey sqlServerKey = Registry.LocalMachine.OpenSubKey( @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" );
        string sqlExpressKeyName = (string) sqlServerKey.GetValue( "SQLEXPRESS" );
        RegistryKey sqlInstanceSetupKey = sqlServerKey.OpenSubKey( sqlExpressKeyName + @"\Setup" );
        return sqlInstanceSetupKey.GetValue( "SQLDataRoot" ).ToString();
    }

Este código funciona bien en nuestras estaciones de trabajo de 32 bits, y funcionó bien en el servidor de compilación hasta que recientemente habilité el análisis de cobertura de código con NCover. Debido a que NCover utiliza un componente COM de 32 bits, el test runner (Gallio) se ejecuta como un proceso de 32 bits.

Comprobando el registro, no hay ninguna clave de "Nombres de instancia" bajo

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft \ Microsoft SQL Server

¿Hay alguna forma de que una aplicación que se ejecuta en modo de 32 bits acceda al registro fuera de Wow6432Node?

Author: Kiquenet, 2009-06-10

5 answers

Debe usar el parámetro KEY_WOW64_64KEY al crear/abrir la clave del registro. Pero AFAIK eso no es posible con la clase Registry, sino solo cuando se usa la API directamente.

Esto podría ayudarte a empezar.

 17
Author: Stefan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-10 07:13:18

Todavía hay soporte nativo para el acceso al registro en Windows de 64 bits utilizando . NET Framework 4.x . El siguiente código se prueba con Windows 7, 64 bit y también con Windows 10, 64 bit . Para acceder al registro de 64 bits , puede usar:

string value64 = string.Empty; 
RegistryKey localKey = 
    RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, 
        RegistryView.Registry64); 
localKey = localKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); 
if (localKey != null) 
{ 
    value64 = localKey.GetValue("RegisteredOrganization").ToString(); 
    localKey.Close();
} 
Console.WriteLine(String.Format("RegisteredOrganization [value64]: {0}",value64));

Si desea acceder al registro de 32 bits, utilice:

string value32 = string.Empty; 
RegistryKey localKey32 = 
    RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, 
        RegistryView.Registry32); 
localKey32 = localKey32.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); 
if (localKey32 != null) 
{ 
    value32 = localKey32.GetValue("RegisteredOrganization").ToString(); 
    localKey32.Close();
} 
Console.WriteLine(String.Format("RegisteredOrganization [value32]: {0}",value32));

No se confunda, ambas versiones están usando Microsoft.Win32.RegistryHive.LocalMachine como primer parámetro, usted hace la distinción si usar 64 bit o 32 bit por el 2do parámetro (RegistryView.Registry64 versus RegistryView.Registry32).

Nótese que

  • En un Windows de 64 bits, HKEY_LOCAL_MACHINE\Software\Wow6432Node contiene los valores utilizados por las aplicaciones de 32 bits que se ejecutan en el sistema de 64 bits. Solo las aplicaciones verdaderas de 64 bits almacenan sus valores en HKEY_LOCAL_MACHINE\Software directamente. El subárbol Wow6432Node es completamente transparente para aplicaciones de 32 bits, las aplicaciones de 32 bits todavía ven HKEY_LOCAL_MACHINE\Software como lo esperan (es una especie de redirección). En mayores versiones de Windows, así como de 32 bits de Windows 7 (y Vista de 32 bits) el subárbol Wow6432Node obviamente no existe .

  • Debido a un error en Windows 7 (64 bits), la versión de código fuente de 32 bits siempre devuelve "Microsoft", independientemente de la organización que haya registrado, mientras que la versión de código fuente de 64 bits devuelve la organización correcta.

Volviendo al ejemplo que ha proporcionado, hágalo de la siguiente manera para acceder al bit 64 rama:

RegistryKey localKey = 
    RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, 
        RegistryView.Registry64); 
RegistryKey sqlServerKey = localKey.OpenSubKey(
    @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL");
string sqlExpressKeyName = (string) sqlServerKey.GetValue("SQLEXPRESS");

Información adicional para uso práctico:

Me gustaría añadir un enfoque interesante Johny Skovdal ha sugerido en los comentarios, que he recogido para desarrollar algunas funciones útiles mediante el uso de su enfoque: En algunas situaciones que desea recuperar todas las teclas, independientemente de si es de 32 bits o 64 bits. Los nombres de instancia SQL son un ejemplo. Puede usar una consulta de unión en ese caso de la siguiente manera (C # 6 o superior):

// using Microsoft.Win32;
public static IEnumerable<string> GetRegValueNames(RegistryView view, string regPath,
                                  RegistryHive hive = RegistryHive.LocalMachine) 
{ 
    return RegistryKey.OpenBaseKey(hive, view)
                     ?.OpenSubKey(regPath)?.G‌​etValueNames();
}

public static IEnumerable<string> GetAllRegValueNames(string RegPath,
                                  RegistryHive hive = RegistryHive.LocalMachine) 
{
    var reg64 = GetRegValueNames(RegistryView.Registry64, RegPath, hive);
    var reg32 = GetRegValueNames(RegistryView.Re‌​gistry32, RegPath, hive);
    var result = (reg64 != null && reg32 != null) ? reg64.Union(reg32) : (reg64 ?? reg32);
    return (result ?? new List<string>().AsEnumerable()).OrderBy(x => x);
}

public static object GetRegValue(RegistryView view, string regPath, string ValueName="",
                                 RegistryHive hive = RegistryHive.LocalMachine)
{
    return RegistryKey.OpenBaseKey(hive, view)
                       ?.OpenSubKey(regPath)?.G‌​etValue(ValueName);
}

public static object GetRegValue(string RegPath, string ValueName="",
                                 RegistryHive hive = RegistryHive.LocalMachine)
{   
    return GetRegValue(RegistryView.Registry64, RegPath, ValueName, hive) 
                     ?? GetRegValue(RegistryView.Re‌​gistry32, RegPath, ValueName, hive);
}

public static IEnumerable<string> GetRegKeyNames(RegistryView view, string regPath,
                   RegistryHive hive = RegistryHive.LocalMachine)
{
    return RegistryKey.OpenBaseKey(hive, view)
        ?.OpenSubKey(regPath)?.GetSubKeyNames(); 
}

public static IEnumerable<string> GetAllRegKeyNames(string RegPath,
                                  RegistryHive hive = RegistryHive.LocalMachine)
{
    var reg64 = GetRegKeyNames(RegistryView.Registry64, RegPath, hive);
    var reg32 = GetRegKeyNames(RegistryView.Re‌​gistry32, RegPath, hive);
    var result = (reg64 != null && reg32 != null) ? reg64.Union(reg32) : (reg64 ?? reg32);
    return (result ?? new List<string>().AsEnumerable()).OrderBy(x => x);
}

Ahora simplemente puede usar las funciones anteriores de la siguiente manera:

var sqlRegPath=@"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL";
foreach (var valueName in GetAllRegValueNames(sqlRegPath))
{
    var value=GetRegValue(sqlRegPath, valueName);
    Console.WriteLine($"{valueName}={value}");
}

Que le dará una lista de los nombres de valores y valores en sqlRegPath.

Nota: Puede acceder al valor predeterminado de una tecla (mostrado por la herramienta de línea de comandos REGEDT32.EXE como (Default)) si omite el parámetro ValueName en las funciones correspondientes anteriores.

Para obtener una lista de subclaves dentro de una clave de registro, use la función GetRegKeyNames o GetAllRegKeyNames. Usted puede usar esta lista para recorrer más claves en el registro.

Por ejemplo

var currentVersionRegPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion";
var uninstallRegPath = $@"{currentVersionRegPath}\Uninstall";
var regKeys = Registry.GetAllRegKeyNames(RegPath: uninstallRegPath);

Obtendrá todas las claves de desinstalación de 32 bits y 64 bits.

Observe el manejo nulo requerido en las funciones porque SQL server se puede instalar como 32 bits o como 64 bits. Las funciones están sobrecargadas por lo que aún puede pasar el parámetro de 32 bits o 64 bits si es necesario; sin embargo, si lo omite, intentará leer 64 bits, si falla (valor nulo), lee los valores de 32 bits.

Aquí hay una especialidad: Porque GetAllRegValueNames normalmente se usa en un contexto de bucle (ver ejemplo anterior), devuelve un enumerable vacío en lugar de null para simplificar los bucles foreach: si no se manejara de esa manera, el bucle tendría que ir precedido por una instrucción if comprobando null lo que sería engorroso tener que hacer eso, de modo que se trata una vez en la función.

¿Por qué molestarse en Null? Porque si no te importa, tendrás un muchos más dolores de cabeza descubrir por qué se lanzó esa excepción de referencia nula en su código-pasaría mucho tiempo averiguando dónde y por qué sucedió. Y si sucedió en producción, estará muy ocupado estudiando archivos de registro o registros de eventos (espero que tenga implementado el registro) ... mejor evitar problemas nulos donde se puede de una manera defensiva. Los operadores ?., ?[...] y ?? pueden ayudarle mucho (ver el código proporcionado anteriormente). Hay un artículo relacionado agradable que discute el nuevo tipos de referencia nullables en C#, que recomiendo leer y también este sobre el operador de Elvis.


Pista: Puede utilizar la edición gratuita de Linqpad para probar todos los ejemplos en Windows. No requiere instalación. No olvide presionar F4 e ingrese Microsoft.Win32 en la pestaña de importación de espacio de nombres. En Visual Studio, necesita using Microsoft.Win32; en la parte superior de su código.

Consejo: Para familiarizarse usted mismo con el nuevo operadores de manejo nulo, pruebe (y depure) el siguiente código en LINQPad:

string[] test { get { return null;} } // property used to return null
void Main()
{
    test.Dump();                    // output: null
    // "elvis" operator:
    test?.Dump();                   // output: 
    // "elvis" operator for arrays
    test?[0].Dump();                // output: 
    (test?[0]).Dump();              // output: null
    // combined with null coalescing operator (brackets required):
    (test?[0]??"<null>").Dump();    // output: "<null>"
}

Si estás interesado, aquí son algunos ejemplos que he reunido mostrando qué más se puede hacer con la herramienta.

 116
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-04 11:35:01

No tengo suficiente rep para comentar, pero vale la pena señalar que funciona al abrir un registro remoto usando OpenRemoteBaseKey. Añadiendo el RegistryView.El parámetro Registry64 permite que un programa de 32 bits en la Máquina A acceda al registro de 64 bits en la máquina B. Antes de pasar ese parámetro, mi programa estaba leyendo el 32 bits después de OpenRemoteBaseKey, y no encontró la clave que buscaba.

Nota: En mi prueba, la máquina remota era en realidad mi máquina, pero accedí a ella a través de OpenRemoteBaseKey, al igual que lo haría para una máquina diferente.

 5
Author: Sandra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-13 17:31:59

Prueba esto (de un proceso de 32 bits):

> %WINDIR%\sysnative\reg.exe query ...

(encontró que aquí ).

 3
Author: akira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-10 12:04:06

Si no puede utilizar. NET 4 con su RegistryKey.OpenBaseKey(..., RegistryView.Registry64), necesita usar la API de Windows directamente.

El interop mínimo es como:

internal enum RegistryFlags
{
    ...
    RegSz = 0x02,
    ...
    SubKeyWow6464Key = 0x00010000,
    ...
}

internal enum RegistryType
{
    RegNone = 0,
    ...
}

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int RegGetValue(
    UIntPtr hkey, string lpSubKey, string lpValue, RegistryFlags dwFlags, 
    out RegistryType pdwType, IntPtr pvData, ref uint pcbData);

Úsalo como:

IntPtr data = IntPtr.Zero;
RegistryType type;
uint len = 0;
RegistryFlags flags = RegistryFlags.RegSz | RegistryFlags.SubKeyWow6464Key;
UIntPtr key = (UIntPtr)((uint)RegistryHive.LocalMachine);

const string subkey= @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL";
const string value = "SQLEXPRESS";

if (RegGetValue(key, subkey, value, flags, out type, data, ref len) == 0)
{
    data = Marshal.AllocHGlobal((int)len);
    if (RegGetValue(key, subkey, value, flags, out type, data, ref len) == 0)
    {
        string sqlExpressKeyName = Marshal.PtrToStringUni(data);
    }
}
 3
Author: Martin Prikryl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-20 16:33:21