Leggere il registro a 64 bit da un’applicazione a 32 bit

Ho un progetto di test dell’unità AC # compilato per AnyCPU. Il nostro build server è una macchina a 64 bit e ha un’istanza SQL Express a 64 bit installata.

Il progetto di test utilizza un codice simile al seguente per identificare il percorso dei file .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(); } 

Questo codice funziona bene sulle nostre workstation a 32 bit e ha funzionato correttamente sul server di build fino a quando non ho recentemente triggersto l’analisi della copertura del codice con NCover. Poiché NCover utilizza un componente COM a 32 bit, il test runner (Gallio) viene eseguito come processo a 32 bit.

Controllando il registro, non c’è la chiave “Instance Names” sotto

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

Esiste un modo per un’applicazione in esecuzione in modalità 32 bit per accedere al registro all’esterno di Wow6432Node?

devi usare il parametro KEY_WOW64_64KEY durante la creazione / apertura della chiave di registro. Ma AFAIK non è ansible con la class Registry ma solo quando si utilizza direttamente l’API.

Questo potrebbe aiutarti a iniziare.

Esiste ancora il supporto nativo per l’accesso al registro in Windows a 64 bit utilizzando .NET Framework 4.x. Il seguente codice è stato testato con Windows 7, 64 bit e anche con Windows 10, 64 bit . Per accedere al registro a 64 bit , è ansible utilizzare:

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

Se si desidera accedere al registro a 32 bit , utilizzare:

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

Non essere confuso, entrambe le versioni utilizzano Microsoft.Win32.RegistryHive.LocalMachine come primo parametro, si distingue se utilizzare 64 bit o 32 bit per il 2 ° parametro ( RegistryView.Registry64 rispetto a RegistryView.Registry32 ).

Notare quello

  • Su Windows a 64 bit, HKEY_LOCAL_MACHINE\Software\Wow6432Node contiene valori utilizzati da applicazioni a 32 bit in esecuzione sul sistema a 64 bit. Solo le applicazioni a 64 bit reali memorizzano i loro valori direttamente in HKEY_LOCAL_MACHINE\Software . Il sottoalbero Wow6432Node è completamente trasparente per applicazioni a 32 bit, le applicazioni a 32 bit continuano a vedere HKEY_LOCAL_MACHINE\Software come previsto (è una sorta di reindirizzamento). Nelle versioni precedenti di Windows e Windows 7 a 32 bit (e Vista a 32 bit) ovviamente il sottoalbero Wow6432Node non esiste.

  • A causa di un bug in Windows 7 (64 bit), la versione del codice sorgente a 32 bit restituisce sempre “Microsoft” indipendentemente da quale organizzazione si è registrato mentre la versione del codice sorgente a 64 bit restituisce l’organizzazione corretta.

Tornando all’esempio che hai fornito, esegui il seguente modo per accedere al ramo a 64 bit:

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

Ulteriori informazioni per l’uso pratico:

Mi piacerebbe aggiungere un approccio interessante che Johny Skovdal ha suggerito nei commenti, che ho raccolto per sviluppare alcune funzioni utili utilizzando il suo approccio: in alcune situazioni si desidera recuperare tutte le chiavi indipendentemente dal fatto che sia 32 bit o 64 bit. I nomi delle istanze SQL sono un esempio. È ansible utilizzare una query di unione in questo caso come segue (C # 6 o successivo):

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

Ora puoi semplicemente utilizzare le funzioni di cui sopra come segue:

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

che ti darà una lista dei valori e dei valori dei valori in sqlRegPath.

Nota: è ansible accedere al valore predefinito di una chiave (visualizzato dallo strumento riga di comando REGEDT32.EXE come (Default) ) se si omette il parametro ValueName nelle funzioni corrispondenti sopra.

Per ottenere un elenco di Sottochiavi all’interno di una chiave del Registro di sistema, utilizzare la funzione GetRegKeyNames o GetAllRegKeyNames . È ansible utilizzare questo elenco per attraversare ulteriori chiavi nel registro.

Per esempio

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

otterrà tutte le chiavi di disinstallazione a 32 bit e 64 bit.

Si noti la gestione nulla richiesta nelle funzioni poiché il server SQL può essere installato come 32 bit o come 64 bit. Le funzioni sono sovraccariche, quindi è ansible passare il parametro a 32 o 64 bit se necessario, tuttavia, se lo si omette, tenterà di leggere 64 bit, se non riesce (valore null), legge i valori a 32 bit.

C’è una specialità qui: Poiché GetAllRegValueNames viene solitamente utilizzato in un contesto di loop (vedi esempio sopra), restituisce un enumerable vuoto anziché null per semplificare i cicli foreach : se non fosse gestito in quel modo, il ciclo dovrebbe essere preceduto da un’istruzione if che verifica il null che sarebbe ingombrante doverlo fare, in modo che venga trattato una volta nella funzione.

Perché preoccuparsi di null? Perché se non ti interessa, avrai molti più grattacapi a scoprire perché quell’eccezione di riferimento nullo è stata gettata nel tuo codice – passeresti molto tempo a scoprire dove e perché è successo. E se è successo in produzione, sarai molto impegnato a studiare i file di registro o i log degli eventi (spero che tu abbia implementato la registrazione) … meglio evitare i problemi null dove puoi in modo difensivo. Gli operatori ?. , ?[] e ?? può aiutarti molto (vedi il codice fornito sopra). C’è un bel articolo correlato che discute i nuovi tipi di riferimento nullable in C # , che consiglio di leggere e anche questo sull’operatore Elvis.


Suggerimento: puoi utilizzare la versione gratuita di Linqpad per testare tutti gli esempi in Windows. Non richiede un’installazione. Non dimenticare di premere F4 e inserire Microsoft.Win32 nella scheda di importazione dello spazio dei nomi. In Visual Studio, è necessario using Microsoft.Win32; nella parte superiore del tuo codice.

Suggerimento: per familiarizzare con i nuovi operatori di gestione null , provare (ed eseguire il debug) il seguente codice in 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: "" } 

Se sei interessato, ecco alcuni esempi che ho messo insieme mostrando cosa altro puoi fare con lo strumento.

Non ho abbastanza rep per commentare, ma vale la pena sottolineare che funziona quando si apre un registro remoto usando OpenRemoteBaseKey. L’aggiunta del parametro RegistryView.Registry64 consente a un programma a 32 bit sulla macchina A di accedere al registro a 64 bit sulla macchina B. Prima di passare tale parametro, il mio programma stava leggendo il 32-bit dopo OpenRemoteBaseKey e non trovava la chiave I era dopo.

Nota: nel mio test, la macchina remota era in realtà la mia macchina, ma l’ho acceduta tramite OpenRemoteBaseKey, proprio come farei per una macchina diversa.

prova questo (da un processo a 32 bit):

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

(l’ho trovato qui ).

Se non è ansible utilizzare .NET 4 con RegistryKey.OpenBaseKey(..., RegistryView.Registry64) , è necessario utilizzare direttamente l’API di Windows.

L’intervallo minimo è come:

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

Usalo come:

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