Odczyt rejestru 64-bitowego z aplikacji 32-bitowej

Mam projekt C# unit test, który jest kompilowany dla AnyCPU. Nasz serwer kompilacji jest 64-bitową maszyną i ma zainstalowaną 64-bitową instancję SQL Express.

Projekt testowy wykorzystuje kod podobny do poniższego, aby zidentyfikować ścieżkę do .Pliki 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();
    }

Ten kod działa dobrze na naszych 32-bitowych stacjach roboczych i działał dobrze na serwerze kompilacji, dopóki nie włączyłem ostatnio analizy pokrycia kodu za pomocą NCover. Ponieważ NCover używa 32-bitowego komponentu COM, Test runner (Gallio) działa jako Proces 32-bitowy.

Sprawdzanie rejestru, nie ma klucza "nazwy instancji" pod

HKEY_LOCAL_MACHINE\SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

Czy istnieje sposób dla aplikacji działającej w trybie 32bit, aby uzyskać dostęp do rejestru poza Wow6432Node?

Author: Kiquenet, 2009-06-10

5 answers

Musisz użyć param KEY_WOW64_64KEY podczas tworzenia / otwierania klucza rejestru. Ale AFAIK to nie jest możliwe z klasą rejestru, ale tylko przy użyciu API bezpośrednio.

To może pomóc Ci zacząć.

 17
Author: Stefan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-10 07:13:18

Nadal istnieje natywne wsparcie dla dostępu do rejestru pod 64-bitowym systemem Windows przy użyciu . NET Framework 4.x . Poniższy kod jest testowany z Windows 7, 64 bit, a także z Windows 10, 64 bit. Aby uzyskać dostęp do rejestru 64-bitowego, możesz użyć:

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

Jeśli chcesz uzyskać dostęp do rejestru 32bit, użyj:

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

Nie myl się, obie wersje używają Microsoft.Win32.RegistryHive.LocalMachine jako pierwszego parametru, rozróżniasz, czy używać 64-bitowy lub 32-bitowy przez drugi parametr (RegistryView.Registry64 versus RegistryView.Registry32).

Uwaga, że

  • W 64-bitowym systemie Windows HKEY_LOCAL_MACHINE\Software\Wow6432Node zawiera wartości używane przez 32-bitowe aplikacje działające w systemie 64-bitowym. Tylko prawdziwe aplikacje 64-bitowe przechowują swoje wartości w HKEY_LOCAL_MACHINE\Software bezpośrednio. Subtree Wow6432Node jest całkowicie przezroczysty dla aplikacji 32-bitowych, aplikacje 32-bitowe nadal widzą HKEY_LOCAL_MACHINE\Software tak, jak tego oczekują (jest to rodzaj przekierowania). W starszych wersje systemu Windows, a także 32-bitowy Windows 7 (i Vista 32-bit) podtrój Wow6432Node oczywiście nie istnieje .

  • Ze względu na błąd w systemie Windows 7 (64-bitowy), 32-bitowa wersja kodu źródłowego zawsze zwraca "Microsoft" niezależnie od zarejestrowanej organizacji, podczas gdy 64-bitowa wersja kodu źródłowego zwraca właściwą organizację.

Wracając do przykładu, który podałeś, zrób to w następujący sposób, aby uzyskać dostęp do 64 bitów oddział:

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

Dodatkowe informacje do praktycznego zastosowania:

Chciałbym dodać ciekawe podejście Johny Skovdal {78]} zasugerował w komentarzach, które wybrałem, aby rozwinąć kilka przydatnych funkcji za pomocą jego podejścia: w niektórych sytuacjach chcesz odzyskać wszystkie klucze, niezależnie od tego, czy jest to 32 bit czy 64 bit. Takim przykładem są nazwy instancji SQL. Można w tym przypadku użyć zapytania union w następujący sposób (C # 6 lub wyżej):

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

Teraz możesz po prostu użyć powyższych funkcji w następujący sposób:

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

Który wyświetli listę nazw wartości i wartości w sqlRegPath.

Uwaga: możesz uzyskać dostęp do domyślnej wartości klucza (wyświetlanej przez narzędzie wiersza poleceń REGEDT32.EXE jako (Default)), jeśli pominiesz parametr ValueName w odpowiednich funkcjach powyżej.

Aby uzyskać listę podkluczy wewnątrz klucza rejestru, użyj funkcji GetRegKeyNames lub GetAllRegKeyNames. Ty może użyć tej listy do przesuwania kolejnych kluczy w rejestrze.

Na przykład

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

Otrzyma wszystkie 32-bitowe i 64-bitowe klucze odinstalowujące.

Zwróć uwagę na obsługę null wymaganą w funkcjach, ponieważ SQL server może być zainstalowany jako 32-bitowy lub 64-bitowy. Funkcje są przeciążone, więc w razie potrzeby można przekazać parametr 32 - bitowy lub 64-bitowy-jednak jeśli go pominiemy, to spróbuje odczytać 64-bitowy, jeśli to się nie powiedzie (wartość null), odczytuje wartości 32-bitowe.

Jest tu jedna specjalność: ponieważ GetAllRegValueNames zwykle jest używany w kontekście pętli (patrz przykład powyżej), zwraca pustą liczbę zamiast null, aby uprościć pętle foreach: gdyby nie była traktowana w ten sposób, pętla musiałaby być poprzedzona poleceniem if sprawdzającym null, co byłoby uciążliwe - więc jest to traktowane raz w funkcji.

Po co zawracać sobie głowę null? bo jeśli cię to nie obchodzi, będziesz miał dużo więcej bólów głowy dowiadując się, dlaczego ten wyjątek null reference został wrzucony do Twojego kodu - spędziłbyś dużo czasu, dowiadując się, gdzie i dlaczego to się stało. A jeśli to się stało w produkcji, będziesz bardzo zajęty studiowaniem plików dziennika lub dzienników zdarzeń (mam nadzieję, że masz logowanie zaimplementowane) ... lepiej unikać problemów null, gdzie można w sposób obronny. Operatorzy ?., ?[...] i ?? mogą ci bardzo pomóc(patrz kod podany powyżej). Jest ładny powiązany artykuł omawiający nowe nullable reference types in C#, which I recommend to read and also this one about the Elvis operator.


Podpowiedź: możesz skorzystać z darmowej edycji Linqpad aby przetestować wszystkie przykłady pod Windows. Nie wymaga instalacji. Nie zapomnij nacisnąć F4 i wpisać Microsoft.Win32 w zakładce import przestrzeni nazw. W Visual Studio potrzebujesz using Microsoft.Win32; u góry kodu.

Wskazówka: aby zapoznać się siebie z nowym operatory obsługi null, wypróbuj (i debuguj) następujący kod w 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>"
}

Jeśli jesteś zainteresowany, proszę. oto kilka przykładów, które zebrałem pokazując, co jeszcze można zrobić z narzędziem.

 116
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-04 11:35:01

Nie mam dość rep do komentowania, ale warto zaznaczyć, że działa to przy otwieraniu zdalnego rejestru za pomocą OpenRemoteBaseKey. Dodawanie widoku rejestracji.Parametr Registry64 pozwala 32-bitowemu programowi na maszynie A na dostęp do 64-bitowego rejestru na maszynie B. zanim przekazałem ten parametr, mój program czytał 32-bitowy po OpenRemoteBaseKey i nie znalazł klucza, którego szukałem.

Uwaga: w moim teście zdalna maszyna była w rzeczywistości moją maszyną, ale uzyskałem do niej dostęp przez OpenRemoteBaseKey, tak jak ja bym dla innej maszyny.

 5
Author: Sandra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 17:31:59

Spróbuj tego (z 32-bitowego procesu):

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

(znalazłem to tutaj).

 3
Author: akira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-10 12:04:06

Jeśli nie możesz używać. NET 4 z jego RegistryKey.OpenBaseKey(..., RegistryView.Registry64), musisz użyć Windows API bezpośrednio.

Minimalny interop to:

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

Użyj go jak:

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
Author: Martin Prikryl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 16:33:21