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

j'ai un projet de test d'unité c qui est compilé pour AnyCPU. Notre serveur de compilation est une machine 64bit, et dispose d'une instance SQL Express 64bit installée.

le projet d'essai utilise un code similaire à celui qui suit pour identifier le chemin vers le .Fichiers MDF:

    private string GetExpressPath()
    {
        RegistryKey sqlServerKey = Registry.LocalMachine.OpenSubKey( @"SOFTWAREMicrosoftMicrosoft SQL ServerInstance NamesSQL" );
        string sqlExpressKeyName = (string) sqlServerKey.GetValue( "SQLEXPRESS" );
        RegistryKey sqlInstanceSetupKey = sqlServerKey.OpenSubKey( sqlExpressKeyName + @"Setup" );
        return sqlInstanceSetupKey.GetValue( "SQLDataRoot" ).ToString();
    }

ce code fonctionne très bien sur nos postes de travail 32bit, et a fonctionné correctement sur le serveur de compilation jusqu'à ce que j'ai récemment activé L'analyse de la couverture de code avec NCover. Parce que NCover utilise un COM 32bit composant, le piston d'essai (Gallio) fonctionne comme un processus de 32 bits.

en vérifiant le registre, il n'y a pas de clé "noms D'Instance" sous

HKEY_LOCAL_MACHINESOFTWAREWow6432NodeMicrosoft SQL Server

y a-t-il un moyen pour une application tournant en mode 32bit d'accéder au registre en dehors de Wow6432Node?

84
demandé sur Kiquenet 2009-06-10 10:48:11

5 réponses

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 de Registre mais seulement en utilisant l'API directement.

ce pourrait vous aider à démarrer.

17
répondu Stefan 2009-06-10 07:13:18

il y a encore le support natif pour l'accès au Registre sous des fenêtres de 64 bits en utilisant .net Framework 4.x . Le code suivant est testé avec Windows 7, 64 bits et aussi avec Windows 10, 64 bits . Pour accéder au 64 registre bit , 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 voulez accéder au registre 32bit , 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 pas confondre, les deux versions utilisent Microsoft.Win32.RegistryHive.LocalMachine comme premier paramètre, vous faites la distinction si à utiliser 64 bits ou 32 bits par le 2ème paramètre ( RegistryView.Registry64 versus RegistryView.Registry32 ).

Note que

  • Sur un 64 bits de Windows, HKEY_LOCAL_MACHINE\Software\Wow6432Node contient des valeurs utilisées par les 32 bits applications tournant sur le système 64 bits. Seules les 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 encore HKEY_LOCAL_MACHINE\Software comme ils s'y attendent (c'est une sorte de redirection). Dans les versions plus anciennes de Windows ainsi que 32 bit Windows 7 (et Vista 32 bit) le sous-Tree Wow6432Node fait évidemment pas existent.

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

pour revenir à l'exemple que vous avez fourni, faites-le de la façon suivante 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 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 indépendamment si elle est de 32 bits ou 64 bits. Les noms D'instance SQL en sont un exemple. Vous pouvez utiliser une requête union dans ce cas comme suit (C#6 ou plus):

// 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:

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 de valeurs et des valeurs dans sqlRegPath.

Note: vous pouvez accéder à la valeur par défaut d'une clé (affichée par l'outil 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 SubKeys dans une clé de Registre, utilisez la fonction GetRegKeyNames ou GetAllRegKeyNames . Vous pouvez utiliser cette liste pour parcourir plus de clés dans le registre.

par exemple

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

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

notez la manipulation nulle requise dans les fonctions parce que SQL server peut être installé en 32 bits ou en 64 bits. Les fonctions sont surchargé de sorte que vous pouvez toujours passer le paramètre 32 bits ou 64 bits si nécessaire - cependant, si vous l'omettez, il va essayer 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 habituellement utilisé dans un contexte de boucle (voir exemple ci-dessus), il renvoie un énumérable vide plutôt que null pour simplifier foreach boucles: si elle ne serait pas traitée de cette façon, la boucle devrait être préfixé par un if vérification de déclaration pour null qui serait lourd à faire cela - de sorte que cela est traité une fois dans la fonction.

pourquoi se préoccuper de null? parce que si vous ne vous souciez pas, vous aurez beaucoup plus de maux de tête à découvrir pourquoi cette exception de référence nulle a été jetée dans votre code - vous passerez beaucoup de temps à découvrir où et pourquoi cela s'est produit. Et si c'est arrivé dans la production, vous serez très occupé à étudier les fichiers journaux ou les journaux d'événements (j'espère que vous avez journalisation mis en œuvre) ... mieux éviter les problèmes nuls où vous pouvez de manière défensive. Les opérateurs ?. , ?[ ... ] et ?? peuvent vous aider beaucoup (voir le code ci-dessus). Il y a un bel article connexe discutant du nouveau types de référence Null dans C# , que je recommande de lire et aussi celui-ci à propos de L'opérateur Elvis.


Conseil: vous pouvez utiliser l'édition gratuite de Linqpad pour tester tous les exemples sous Windows. Elle ne nécessite pas d'installation. N'oubliez pas d'appuyer sur F4 et d'entrer Microsoft.Win32 dans l'onglet importation D'espace de noms. Dans Visual Studio, vous avez besoin de using Microsoft.Win32; en haut de votre code.

Conseil: À familiarisez-vous avec le nouveau "null handling operators , essayez (et déboguez) le code suivant dans 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 vous êtes intéressé, ici sont quelques exemples que j'ai mis ensemble montrant ce que vous pouvez faire d'autre avec l'outil.

116
répondu Matt 2018-09-04 11:35:01

Je n'ai pas assez de rep pour commenter, mais il est intéressant de souligner que cela fonctionne lors de l'ouverture d'un Registre distant en utilisant OpenRemoteBaseKey. L'ajout de la RegistryView.Le paramètre Registry64 permet à un programme 32 bits de la Machine A d'accéder au Registre 64 bits de 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é que je cherchais.

Note: dans 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
répondu Sandra 2015-01-13 17:31:59

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

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

(trouvé que ici ).

3
répondu akira 2010-05-10 12:04:06

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

Le minimum d'interopérabilité, c'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);

utiliser 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);
    }
}
3
répondu Martin Prikryl 2018-08-20 16:33:21