wake-up-neo.com

64-Bit-Registrierung aus einer 32-Bit-Anwendung lesen

Ich habe ein C # Unit-Testprojekt, das für AnyCPU kompiliert ist. Unser Build-Server ist eine 64-Bit-Maschine, auf der eine 64-Bit-SQL Express-Instanz installiert ist.

Das Testprojekt verwendet folgenden Code, um den Pfad zu den .MDF-Dateien zu ermitteln:

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

Dieser Code funktioniert einwandfrei auf unseren 32-Bit-Workstations und hat auf dem Build-Server einwandfrei funktioniert, bis ich kürzlich die Code-Coverage-Analyse mit NCover aktiviert habe. Da NCover eine 32-Bit-COM-Komponente verwendet, wird der Testrunner (Gallio) als 32-Bit-Prozess ausgeführt.

Bei der Überprüfung der Registrierung befindet sich kein "Instanznamen" -Schlüssel unter

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server

Gibt es eine Möglichkeit für eine Anwendung, die im 32-Bit-Modus ausgeführt wird, auf die Registrierung außerhalb von Wow6432Node zuzugreifen?

89
David Gardiner

sie müssen den Parameter KEY_WOW64_64KEY zum Erstellen/Öffnen des Registrierungsschlüssels verwenden. Aber AFAIK ist mit der Registry-Klasse nicht möglich, sondern nur, wenn die API direkt verwendet wird.

Dies könnte Ihnen den Einstieg erleichtern.

19
Stefan

Es gibt immer noch native Unterstützung für den Registrierungszugriff unter 64-Bit-Windows mit . NET Framework 4.x. Der folgende Code wird mit Windows 7, 64 Bit und auch mit Windows 10, 64 Bit getestet. Um auf die 64-Bit-Registrierung zuzugreifen, können Sie Folgendes verwenden:

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

Wenn Sie auf die 2-Bit-Registrierung zugreifen möchten, verwenden Sie:

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

Seien Sie nicht verwirrt, beide Versionen verwenden Microsoft.Win32.RegistryHive.LocalMachine Als ersten Parameter. Sie treffen die Unterscheidung, ob 64-Bit ​​oder 2-Bit ​​durch - verwendet wird. 2. Parameter (RegistryView.Registry64 Versus RegistryView.Registry32).

Anmerkung das

  • Unter 64-Bit-Windows enthält HKEY_LOCAL_MACHINE\Software\Wow6432Node Werte, die von 32-Bit-Anwendungen verwendet werden, die auf dem 64-Bit-System ausgeführt werden. Nur echte 64-Bit-Anwendungen speichern ihre Werte direkt in HKEY_LOCAL_MACHINE\Software. Der Unterbaum Wow6432Node Ist für 32-Bit-Anwendungen vollständig transparent, 32-Bit-Anwendungen sehen immer noch HKEY_LOCAL_MACHINE\Software So, wie sie es erwarten (es ist eine Art Umleitung). In älteren Windows-Versionen sowie in 32-Bit-Versionen von Windows 7 (und Vista 32-Bit) ist der Unterbaum Wow6432Node Offensichtlich nicht ​​vorhanden.

  • Aufgrund eines Fehlers in Windows 7 (64 Bit) gibt die 32-Bit-Quellcodeversion immer "Microsoft" zurück, unabhängig davon, welche Organisation Sie registriert haben, während die 64-Bit-Quellcodeversion die richtige Organisation zurückgibt.

Gehen Sie zum angegebenen Beispiel folgendermaßen vor, um auf den 64-Bit-Zweig zuzugreifen:

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

Zusätzliche Informationen für den praktischen Gebrauch:

Ich möchte einen interessanten Ansatz hinzufügen Johny Skovdal hat in den Kommentaren vorgeschlagen, die ich aufgegriffen habe, um mithilfe seines Ansatzes einige nützliche Funktionen zu entwickeln: In einigen Situationen möchten Sie alle Schlüssel zurückerhalten egal ob es 32 bit oder 64 bit ist. Die SQL-Instanznamen sind ein solches Beispiel. In diesem Fall können Sie eine Unionsabfrage wie folgt verwenden (C # 6 oder höher):

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

Jetzt können Sie die oben genannten Funktionen einfach wie folgt verwenden:

Beispiel 1: SQL-Instanznamen abrufen

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

sie erhalten eine Liste der Wertnamen und -werte in sqlRegPath.

Hinweis: Sie können auf den Standard Wert eines Schlüssels zugreifen (angezeigt vom Kommandozeilen-Tool REGEDT32.EXE Als (Default)), Wenn Sie das ValueName Parameter in den entsprechenden Funktionen oben.

Verwenden Sie die Funktion GetRegKeyNames oder GetAllRegKeyNames, um eine Liste von SubKeys innerhalb eines Registrierungsschlüssels abzurufen. Mit dieser Liste können Sie weitere Schlüssel in der Registrierung durchlaufen.

Beispiel 2: Informationen zur Deinstallation der installierten Software abrufen

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

erhält alle 32-Bit- und 64-Bit-Deinstallationsschlüssel.

Beachten Sie die Nullbehandlung erforderlich in den Funktionen, da SQL Server als 32-Bit oder als 64-Bit installiert werden kann (Beispiel 1 oben). Die Funktionen sind überladen, sodass Sie den 32-Bit- oder 64-Bit-Parameter bei Bedarf weitergeben können. Wenn Sie ihn jedoch weglassen, wird versucht, 64-Bit-Werte zu lesen. Wenn dies fehlschlägt (Nullwert), werden die 32-Bit-Werte gelesen.

Hier gibt es eine Besonderheit: Da GetAllRegValueNames normalerweise in einem Schleifenkontext verwendet wird (siehe Beispiel 1 oben), wird statt null eine leere Aufzählung zurückgegeben, um foreach - Schleifen: Wenn dies nicht so gehandhabt würde, müsste der Schleife eine if - Anweisung vorangestellt werden, die prüft, ob null vorhanden ist. Dies wäre umständlich. damit wird in der funktion einmalig umgegangen.

Warum sich um null kümmern? Wenn Sie sich nicht darum kümmern, werden Sie viel mehr Kopfschmerzen haben, wenn Sie herausfinden, warum diese Nullreferenzausnahme in Ihrem Code geworfen wurde - Sie würden viel Zeit damit verbringen, nach ihr zu suchen raus wo und warum es passiert ist. Und wenn es in der Produktion passiert ist, werden Sie sehr beschäftigt sein, Protokolldateien oder Ereignisprotokolle zu studieren (ich hoffe, Sie haben die Protokollierung implementiert). Vermeiden Sie besser Null-Probleme, wo Sie es auf defensive Weise können. Die Operatoren ?., ?[ ... ] Und ?? Können Ihnen sehr helfen (siehe obigen Code). Es gibt einen Artikel zum Thema Nizza, in dem die neuen nullbaren Verweistypen in C # behandelt werden. Ich empfehle, diesen Artikel zu lesen, und auch diesen über den Elvis-Operator.


Hinweis: Mit der kostenlosen Version von Linqpad können Sie alle Beispiele unter Windows testen. Es ist keine Installation erforderlich. Vergiss nicht zu drücken F4 und geben Sie Microsoft.Win32 auf der Registerkarte Namespace-Import ein. In Visual Studio benötigen Sie using Microsoft.Win32; Am oberen Rand Ihres Codes.

Tipp: Um sich mit den neuen Nullbehandlungsoperatoren vertraut zu machen, probieren Sie den folgenden Code in LinqPad aus (und debuggen Sie ihn):

Beispiel 3: Veranschaulichen von Nullbehandlungsoperatoren

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

Probieren Sie es mit .Net Geige

Wenn Sie interessiert sind, sind hier einige Beispiele, die ich zusammengestellt habe und die zeigen, was Sie mit dem Tool noch alles tun können.

129
Matt

Ich habe nicht genug Repräsentanten, um einen Kommentar abzugeben, aber es ist erwähnenswert, dass dies funktioniert, wenn eine Remote-Registrierung mit OpenRemoteBaseKey geöffnet wird. Durch Hinzufügen des Parameters RegistryView.Registry64 kann ein 32-Bit-Programm auf Computer A auf die 64-Bit-Registrierung auf Computer B zugreifen. Bevor ich diesen Parameter übergeben habe, hat mein Programm die 32-Bit-Version nach OpenRemoteBaseKey gelesen und den Schlüssel I nicht gefunden war danach.

Hinweis: In meinem Test war der Remote-Computer eigentlich mein Computer, aber ich habe über OpenRemoteBaseKey darauf zugegriffen, genau wie bei einem anderen Computer.

5
Sandra

versuchen Sie dies (von einem 32-Bit-Prozess):

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

(fand das hier ).

4
akira

Wenn Sie .NET 4 mit RegistryKey.OpenBaseKey(..., RegistryView.Registry64) nicht verwenden können, müssen Sie die Windows-API direkt verwenden.

Die minimale Interop ist wie folgt:

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

Verwenden Sie es wie folgt:

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