2009-06-10 17 views
78

Tengo un proyecto de prueba de unidad C# que se compila para AnyCPU. Nuestro servidor de compilación es una máquina de 64 bits y tiene instalada una instancia SQL Express de 64 bits.Lectura de 64 bits Registro desde una aplicación de 32 bits

El proyecto de prueba utiliza un código similar al siguiente para identificar la ruta de los 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 lo hizo bien el trabajo en la construcción de servidor hasta que recientemente permitió el análisis de cobertura de código con NCover. Debido a que NCover usa un componente COM de 32 bits, el corredor de prueba (Gallio) se ejecuta como un proceso de 32 bits.

Comprobación del registro, no hay ninguna clave "nombres de instancia" bajo

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

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

Respuesta

16

tiene que usar el parametro KEY_WOW64_64KEY al crear/abrir la clave de registro. Pero AFAIK no es posible con la clase Registry, pero solo cuando se usa la API directamente.

This podría ayudarlo a comenzar.

3

probar esto (de un proceso de 32 bits):

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

(encontró que here).

108

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

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(); 
} 
Console.WriteLine(String.Format("RegisteredOrganization [value64]: {0}",value64)); 

Si desea tener acceso 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(); 
} 
Console.WriteLine(String.Format("RegisteredOrganization [value32]: {0}",value32)); 

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

Nota que

  • En un Windows de 64 bits, HKEY_LOCAL_MACHINE\Software\Wow6432Node contiene 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 aún ven HKEY_LOCAL_MACHINE\Software como lo esperan (es una especie de redirección). En las versiones anteriores de Windows, así como en Windows 7 de 32 bits (y en Vista de 32 bits), el subárbol Wow6432Node obviamente no contiene ni.

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

Volviendo al ejemplo que nos ha facilitado, hacerlo de la siguiente manera de acceder a la rama 64 bits:

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"); 

Actualización:

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

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); 
} 

Ahora usted puede simplemente utilizar las funciones antes de la siguiente manera:

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

el cual le dará una lista de los nombres y valores de valor en sqlRegPath.

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

Hay una especialidad aquí: Debido GetAllRegValueNames se utiliza generalmente en un contexto de bucle (véase el ejemplo anterior), devuelve un vacío enumerable en lugar de null para simplificar foreach bucles: si no se maneja de esa manera, el bucle debería ir precedido por una declaración if que compruebe null, lo que sería engorroso tener que hacer eso, por lo que se trata una vez en la función.

¿Por qué molestarse por null? Porque si no te importa, tendrás muchos más dolores de cabeza al descubrir por qué esa excepción de referencia nula fue lanzada en tu código: pasarías mucho tiempo buscando dónde y por qué sucedió. Y si sucedió en producción, estarás muy ocupado estudiando archivos de registro o registros de eventos (espero que hayas implementado el registro) ... mejor evita los problemas nulos donde puedas de forma defensiva. Los operadores ?., ?[ ... ] y ?? pueden ayudarlo mucho (consulte el código provisto arriba).


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

Consejo: a familiarizarse con los nuevos operadores de handling nulos, probar (y depuración) 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á interesado, here son algunos ejemplos que reuní para mostrar qué más puedes hacer con la herramienta.

+2

Gracias por su exhaustiva respuesta. De memoria, creo que estaba usando .NET 3.5 cuando publiqué la pregunta, pero es bueno ver que .NET 4 ha mejorado la situación –

+2

De nada. Recientemente tuve un problema similar con el registro de 64 bits que ya había resuelto, así que pensé que valía la pena compartir la solución. – Matt

+2

Esto es exactamente lo que he estado buscando. Estoy haciendo esto en Windows 9.1 y funciona muy bien. –

4

No tengo suficientes representantes para comentar, pero vale la pena señalar que funciona al abrir un registro remoto utilizando OpenRemoteBaseKey. Agregar el parámetro RegistryView.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 los 32 bits después de OpenRemoteBaseKey, y no encontré la clave I fue después.

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

3

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

La interoperabilidad es como mínimo:

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); 

usarlo 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); 
    } 
} 
Cuestiones relacionadas