web-dev-qa-db-fra.com

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

J'ai un projet de test unitaire c # qui est compilé pour AnyCPU. Notre serveur de génération est une machine 64 bits et dispose d'une instance SQL Express 64 bits installée.

Le projet de test utilise un code similaire au suivant pour identifier le chemin d'accès aux fichiers .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();
    }

Ce code fonctionne bien sur nos postes de travail 32 bits et fonctionnait correctement sur le serveur de génération jusqu'à ce que j'active récemment l'analyse de la couverture du code avec NCover. Étant donné que NCover utilise un composant COM 32 bits, le testeur (Gallio) s'exécute en tant que processus 32 bits.

Vérification du 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 une application s'exécutant en mode 32 bits d'accéder au registre en dehors de Wow6432Node?

89
David Gardiner

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

This pourrait vous aider à démarrer.

19
Stefan

Il existe toujours un support natif pour l'accès au registre sous Windows 64 bits à l'aide de .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:

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 vous souhaitez accéder au registre 32 bits , utilisez:

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

Ne vous y trompez pas, les deux versions utilisent Microsoft.Win32.RegistryHive.LocalMachine comme premier paramètre, vous faites la distinction entre utiliser 64 bits ou 32 bits par le 2ème paramètre (RegistryView.Registry64 contre RegistryView.Registry32).

Notez que

  • Sous 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 leurs valeurs dans HKEY_LOCAL_MACHINE\Software directement. 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 l'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 évidemment n'existe pas .

  • En raison d'un bogue dans Windows 7 (64 bits), la version de code source 32 bits renvoie toujours "Microsoft" quelle que soit l'organisation que vous avez enregistrée tandis que la version de code source 64 bits renvoie la bonne organisation.

Pour revenir à l'exemple que vous avez fourni, procédez comme suit pour accéder à la branche 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");

Informations supplémentaires pour une utilisation pratique:

Je voudrais ajouter une approche intéressante Johny Skovdal a suggéré dans les commentaires, que j'ai repris pour développer quelques fonctions utiles en utilisant son approche: Dans certaines situations, vous voulez récupérer toutes les clés qu'il s'agisse de 32 bits ou 64 bits. 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<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);
}

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

Exemple 1: Récupération des noms d'instance SQL

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

vous donnera une liste des noms de valeurs et des valeurs 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 comme (Default)) si vous omettez le paramètre ValueName dans les fonctions correspondantes ci-dessus.

Pour obtenir une liste de sous-clés dans une clé de registre, utilisez la fonction GetRegKeyNamesou GetAllRegKeyNames. Vous pouvez utiliser cette liste pour parcourir d'autres clés dans le registre.

Exemple 2: Obtenez les informations de désinstallation des logiciels installés

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 nulle requise dans les fonctions car SQL Server peut être installé en 32 bits ou en 64 bits (exemple 1 ci-dessus). Les fonctions sont surchargées de sorte que vous pouvez 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: parce que GetAllRegValueNames est généralement utilisé dans un contexte de boucle (voir l'exemple 1 ci-dessus), il retourne un énumérable vide plutôt que null pour simplifier les boucles foreach: si elle n'était pas gérée de cette façon, la boucle devrait être préfixée par une instruction if vérifiant null ce qui serait fastidieux de devoir le faire - ce qui est traité une fois dans la fonction.

Pourquoi se préoccuper de null? Parce que si vous ne vous en souciez pas, vous aurez beaucoup plus de maux de tête pour découvrir pourquoi cette exception de référence null a été lancée dans votre code - vous passeriez beaucoup de temps à découvrir où et pourquoi cela s'est produit. Et si cela s'est produit en production, vous serez très occupé à étudier les fichiers journaux ou les journaux d'événements (j'espère que la journalisation est implémentée) ... mieux éviter les problèmes nuls où vous pouvez de manière défensive. Les opérateurs ?., ?[...] et ?? peut vous aider beaucoup (voir le code fourni ci-dessus). Il y a un article sur Nice qui discute du nouveau types de référence nullable en C # , que je recommande de lire et aussi celui-ci 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 d'espace de noms. Dans Visual Studio, vous avez besoin de using Microsoft.Win32; en haut de votre code.

Astuce: Pour vous familiariser avec les nouveaux opérateurs de gestion nuls , essayez (et déboguez) le code suivant dans LinqPad:

Exemple 3: Démonstration des opérateurs de gestion nuls

static string[] test { get { return null;} } // property used to return null
static 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>"
}

Essayez-le avec le violon .Net

Si vous êtes intéressé, ici sont quelques exemples que j'ai rassemblés montrant ce que vous pouvez faire d'autre avec l'outil.

129
Matt

Je n'ai pas assez de représentant pour commenter, mais il convient de souligner que cela fonctionne lors de l'ouverture d'un registre distant à l'aide d'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 passer ce paramètre, mon programme lisait le 32 bits après OpenRemoteBaseKey et n'a pas trouvé la clé I était après.

Remarque: Lors de mon test, la machine distante était en fait ma machine, mais j'y ai accédé via OpenRemoteBaseKey, comme je le ferais pour une autre machine.

5
Sandra

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

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

(trouvé que ici ).

4
akira

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

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, string lpSubKey, string 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 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);
    }
}
4
Martin Prikryl