Leyendo el Registro de 64 bits desde una aplicación de 32 bits

Tengo un proyecto de prueba de unidad de CA que se comstack para AnyCPU. Nuestro servidor de comstackción es una máquina de 64 bits y tiene instalada una instancia SQL Express de 64 bits.

El proyecto de prueba utiliza un código similar al siguiente para identificar la ruta a 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 funcionó bien en el servidor de comstackción hasta que recientemente habilité 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.

Verificando el registro, no hay una clave de “Nombre de instancia” en

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

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

debe usar el parámetro 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.

Esto podría ayudarlo a comenzar.

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 , use:

 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 usan Microsoft.Win32.RegistryHive.LocalMachine como primer parámetro, usted hace la distinción entre usar 64 bit o 32 bit en el segundo parámetro ( RegistryView.Registry64 versus RegistryView.Registry32 ).

Tenga en cuenta 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 las 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 versiones anteriores de Windows, así como en Windows 7 de 32 bits (y Vista de 32 bits), el subárbol Wow6432Node obviamente no existe.

  • Debido a un error en Windows 7 (64 bits), 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 ha proporcionado, hágalo de la siguiente manera para acceder a la twig de 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"); 

Información adicional para uso práctico:

Me gustaría agregar un enfoque interesante que Johny Skovdal ha sugerido en los comentarios, que he recogido para desarrollar algunas funciones útiles mediante su enfoque: en algunas situaciones, quiere recuperar todas las claves independientemente de si son de 32 bits o 64 bit. 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 GetRegValueNames(RegistryView view, string regPath, RegistryHive hive = RegistryHive.LocalMachine) { return RegistryKey.OpenBaseKey(hive, view) ?.OpenSubKey(regPath)?.G‌​etValueNames(); } public static IEnumerable 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().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 GetRegKeyNames(RegistryView view, string regPath, RegistryHive hive = RegistryHive.LocalMachine) { return RegistryKey.OpenBaseKey(hive, view) ?.OpenSubKey(regPath)?.GetSubKeyNames(); } public static IEnumerable 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().AsEnumerable()).OrderBy(x => x); } 

Ahora puede simplemente 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 y valores de valor en sqlRegPath.

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

Para obtener una lista de SubKeys dentro de una clave de registro, use la función GetRegKeyNames o GetAllRegKeyNames . Puede usar esta lista para recorrer otras 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 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.

Aquí hay una especialidad: GetAllRegValueNames que GetAllRegValueNames se usa generalmente en un contexto de bucle (ver el 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 ser prefijado por una instrucción if que comprueba null que sería engorroso tener que hacer eso, por lo que se trata una vez en la función.

¿Por qué molestarse por nulo? 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 averiguando 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 ?? puede ayudarlo mucho (vea el código provisto arriba). Hay un buen artículo relacionado que analiza los nuevos tipos de referencia que aceptan valores nulables en C # , que recomiendo leer y también este sobre el operador de Elvis.


Sugerencia: 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 tu código

Consejo: Para familiarizarse con los nuevos 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]??"").Dump(); // output: "" } 

Si está interesado, aquí hay algunos ejemplos que reuní para mostrar qué más puede hacer con la herramienta.

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 progtwig 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 progtwig 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.

prueba esto (de un proceso de 32 bits):

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

(encontrado que aquí ).

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

La interoperabilidad mínima 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); 

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