Lecture du registre 64 bits à partir d’une application 32 bits

J’ai un projet de test unitaire ac compilé pour AnyCPU. Notre serveur de compilation est une machine 64 bits et une instance SQL Express 64 bits est installée.

Le projet de test utilise un code similaire à celui-ci pour identifier le chemin d’access aux fichiers .MDF:

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

Ce code fonctionne bien sur nos stations de travail 32 bits et fonctionne correctement sur le serveur de compilation jusqu’à ce que je permette l’parsing de couverture de code avec NCover. Étant donné que NCover utilise un composant COM 32 bits, le programme d’exécution (Gallio) s’exécute en tant que processus 32 bits.

En vérifiant le registre, il n’y a pas de clé “Noms d’instance” sous

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

Existe-t-il un moyen pour qu’une application s’exécutant en mode 32 bits puisse accéder au registre en dehors de Wow6432Node?

vous devez utiliser le paramètre KEY_WOW64_64KEY lors de la création / ouverture de la clé de registre. Mais AFAIK n’est pas possible avec la classe Registry mais uniquement lorsque vous utilisez directement l’API.

Cela pourrait vous aider à démarrer.

Il existe toujours un support natif pour l’access au registre sous Windows 64 bits en utilisant .NET Framework 4.x. Le code suivant est testé avec Windows 7, 64 bits et également avec Windows 10, 64 bits . Pour accéder au registre 64 bits , vous pouvez utiliser:

 ssortingng value64 = ssortingng.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").ToSsortingng(); localKey.Close(); } Console.WriteLine(Ssortingng.Format("RegisteredOrganization [value64]: {0}",value64)); 

Si vous souhaitez accéder au registre 32 bits , utilisez:

 ssortingng value32 = ssortingng.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").ToSsortingng(); localKey32.Close(); } Console.WriteLine(Ssortingng.Format("RegisteredOrganization [value32]: {0}",value32)); 

Ne soyez pas confus, les deux versions utilisent Microsoft.Win32.RegistryHive.LocalMachine comme premier paramètre, vous faites la distinction entre utiliser 64 bits ou 32 bits avec le deuxième paramètre ( RegistryView.Registry64 et RegistryView.Registry32 ).

Notez que

  • Sur Windows 64 bits, HKEY_LOCAL_MACHINE\Software\Wow6432Node contient les valeurs utilisées par les applications 32 bits exécutées sur le système 64 bits. Seules les vraies applications 64 bits stockent directement leurs valeurs dans HKEY_LOCAL_MACHINE\Software . Le sous-arbre Wow6432Node est entièrement transparent pour les applications 32 bits. Les applications 32 bits voient toujours HKEY_LOCAL_MACHINE\Software comme ils s’y attendent (c’est une sorte de redirection). Dans les anciennes versions de Windows ainsi que Windows 7 32 bits (et Vista 32 bits), le sous-arbre Wow6432Node n’existait évidemment pas .

  • En raison d’un bogue dans Windows 7 (64 bits), la version du code source 32 bits renvoie toujours “Microsoft” quelle que soit l’organisation que vous avez enregistrée, alors que la version du code source 64 bits renvoie la bonne organisation.

Pour revenir à l’exemple que vous avez fourni, procédez comme suit pour accéder à la twig 64 bits:

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

Informations supplémentaires pour une utilisation pratique:

Je voudrais append une approche intéressante suggérée par Johny Skovdal dans les commentaires, que j’ai repris pour développer des fonctions utiles en utilisant son approche: Dans certaines situations, vous voulez récupérer toutes les clés, que ce soit 32 bits ou 64 bit. Les noms d’instance SQL en sont un exemple. Vous pouvez utiliser une requête d’union dans ce cas comme suit (C # 6 ou supérieur):

 // using Microsoft.Win32; public static IEnumerable GetRegValueNames(RegistryView view, ssortingng regPath, RegistryHive hive = RegistryHive.LocalMachine) { return RegistryKey.OpenBaseKey(hive, view) ?.OpenSubKey(regPath)?.G‌​etValueNames(); } public static IEnumerable GetAllRegValueNames(ssortingng 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, ssortingng regPath, ssortingng ValueName="", RegistryHive hive = RegistryHive.LocalMachine) { return RegistryKey.OpenBaseKey(hive, view) ?.OpenSubKey(regPath)?.G‌​etValue(ValueName); } public static object GetRegValue(ssortingng RegPath, ssortingng 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, ssortingng regPath, RegistryHive hive = RegistryHive.LocalMachine) { return RegistryKey.OpenBaseKey(hive, view) ?.OpenSubKey(regPath)?.GetSubKeyNames(); } public static IEnumerable GetAllRegKeyNames(ssortingng 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); } 

Maintenant, vous pouvez simplement utiliser les fonctions ci-dessus comme suit:

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

qui vous donnera une liste des noms et des valeurs de valeur dans sqlRegPath.

Remarque: Vous pouvez accéder à la valeur par défaut d’une clé (affichée par l’outil de ligne de commande REGEDT32.EXE sous la forme (Default) ) si vous omettez le paramètre ValueName dans les fonctions correspondantes ci-dessus.

Pour obtenir une liste de SubKeys dans une clé de registre, utilisez la fonction GetRegKeyNames ou GetAllRegKeyNames . Vous pouvez utiliser cette liste pour parcourir d’autres clés du registre.

Par exemple

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

obtiendra toutes les clés de désinstallation 32 bits et 64 bits.

Notez la gestion NULL requirejse dans les fonctions car SQL Server peut être installé en tant que 32 bits ou 64 bits. Les fonctions sont surchargées, vous pouvez donc toujours passer le paramètre 32 bits ou 64 bits si nécessaire – cependant, si vous l’omettez, il essaiera de lire 64 bits, si cela échoue (valeur nulle), il lit les valeurs 32 bits.

Il y a une spécialité ici: GetAllRegValueNames étant généralement utilisé dans un contexte de boucle (voir exemple ci-dessus), il retourne une énumération vide plutôt que null pour simplifier les boucles foreach : si elle n’était pas gérée de cette manière, la boucle devrait être préfixé par une instruction if vérifiant la valeur null ce qui serait fastidieux de le faire – donc cela est traité une fois dans la fonction.

Pourquoi se soucier de null? Parce que si vous vous en fichez, vous aurez beaucoup plus de maux de tête pour savoir pourquoi cette exception de référence nulle a été ajoutée à votre code – vous passeriez beaucoup de temps à trouver où et pourquoi cela s’est produit. Et si cela se produisait en production, vous serez très occupé à étudier les fichiers journaux ou les journaux des événements (j’espère que la journalisation est implémentée) … évitez les problèmes nuls lorsque vous le pouvez de manière défensive. Les opérateurs ?. , ?[] et ?? peut vous aider beaucoup (voir le code ci-dessus). Il existe un article lié intéressant sur les nouveaux types de référence nullables en C # , que je vous recommande de lire, ainsi que sur l’opérateur Elvis.


Astuce: vous pouvez utiliser l’édition gratuite de Linqpad pour tester tous les exemples sous Windows. Il ne nécessite pas d’installation. N’oubliez pas d’appuyer sur F4 et entrez Microsoft.Win32 dans l’onglet d’importation Namespace. Dans Visual Studio, vous devez using Microsoft.Win32; en haut de votre code.

Astuce: Pour vous familiariser avec les nouveaux opérateurs de gestion de null , essayez le code suivant dans LinqPad:

 ssortingng[] 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 vous êtes intéressé, voici quelques exemples que je vous propose de montrer.

Je n’ai pas assez de rep pour commenter, mais il convient de souligner que cela fonctionne lors de l’ouverture d’un registre distant en utilisant OpenRemoteBaseKey. L’ajout du paramètre RegistryView.Registry64 permet à un programme 32 bits sur la machine A d’accéder au registre 64 bits sur la machine B. Avant de transmettre ce paramètre, mon programme lisait 32 bits après OpenRemoteBaseKey et ne trouvait pas la clé I était après.

Note: Dans mon test, la machine distante était en fait ma machine, mais je l’ai accédée via OpenRemoteBaseKey, comme je le ferais pour une machine différente.

essayez ceci (à partir d’un processus 32 bits):

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

(trouvé ça ici ).

Si vous ne pouvez pas utiliser .NET 4 avec son RegistryKey.OpenBaseKey(..., RegistryView.Registry64) , vous devez utiliser l’API Windows directement.

L’interopérabilité minimale est comme:

 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, ssortingng lpSubKey, ssortingng lpValue, RegistryFlags dwFlags, out RegistryType pdwType, IntPtr pvData, ref uint pcbData); 

Utilisez-le comme:

 IntPtr data = IntPtr.Zero; RegistryType type; uint len = 0; RegistryFlags flags = RegistryFlags.RegSz | RegistryFlags.SubKeyWow6464Key; UIntPtr key = (UIntPtr)((uint)RegistryHive.LocalMachine); const ssortingng subkey= @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"; const ssortingng 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) { ssortingng sqlExpressKeyName = Marshal.PtrToSsortingngUni(data); } }