Чтение 64-битного реестра из 32-битного приложения



У меня есть проект модульного теста c#, который скомпилирован для AnyCPU. Наш сервер сборки является 64-битной машиной и имеет 64-битный экземпляр SQL Express.



тестовый проект использует код, подобный приведенному ниже, чтобы определить путь к объекту .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();
}


этот код отлично работает на наших 32-битных рабочих станциях и работал нормально на сервере сборки, пока я недавно не включил анализ покрытия кода с помощью NCover. Поскольку NCover использует 32-битный COM-компонент, тестовый запуск (Gallio) работает как 32-битный процесс.



проверка реестра, нет ключа "имена экземпляров" под




раздел HKEY_LOCAL_MACHINEпрограммное обеспечениеWow6432NodeМайкрософткорпорация Майкрософт SQL сервер




есть ли способ для приложения, работающего в 32-битном режиме, чтобы получить доступ к реестру за пределами Wow6432Node?

800   5  

5 ответов:

вы должны использовать KEY_WOW64_64KEY param при создании / открытии раздела реестра. Но AFAIK это невозможно с помощью класса реестра, но только при непосредственном использовании API.

этой может помочь вам начать работу.

по-прежнему существует встроенная поддержка доступа к реестру под 64-разрядной Windows с помощью .NET Framework 4.x. Следующий код тестируется с помощью Windows 7, 64 бит а также с ОС Windows 10, 64-разрядная. Чтобы получить доступ к 64-битный реестр, вы можете использовать:

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

если вы хотите получить доступ к 32bit registry использовать:

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

не путайте, обе версии используют Microsoft.Win32.RegistryHive.LocalMachine как первый параметр, вы делаете различие, следует ли использовать 64 бит или 32 бит на 2-й параметр (RegistryView.Registry64 и RegistryView.Registry32).

Примечание это

  • на 64-битной Windows,HKEY_LOCAL_MACHINE\Software\Wow6432Node содержит значения, используемые 32-разрядными приложениями, работающими в 64-разрядной системе. Только истинные 64-битные приложения хранят свои значения в HKEY_LOCAL_MACHINE\Software напрямую. Поддерево Wow6432Node полностью прозрачен для 32 бит приложения 32 битные приложения все еще вижу HKEY_LOCAL_MACHINE\Software как они и ожидали (это своего рода перенаправление). В более старых версиях Windows, а также 32 бит Windows 7 (и Vista 32 бит) поддерево Wow6432Node конечно же не.

  • из-за ошибки в Windows 7 (64 бит), 32-разрядная версия исходного кода всегда возвращает "Microsoft" независимо от того, какую организацию вы зарегистрировали в то время как 64-разрядная версия исходного кода возвращает право организация.

возвращаясь к примеру, который вы предоставили, сделайте это следующим образом, чтобы получить доступ к 64-битной ветви:

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

дополнительная информация для практического применения:

я хотел бы добавить интересный подход Джони Skovdal предложил в комментариях, которые я подобрал, чтобы разработать некоторые полезные функции, используя его подход: в некоторых ситуациях вы хотите вернуть все ключи независимо от того, является ли это 32 бит или 64 бит. Таким примером являются имена экземпляров SQL. Вы можете использовать запрос объединения в этом случае следующим образом (C#6 или выше):

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

теперь вы можете просто использовать функции выше следующим образом:

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

который даст вам список имен значений и значений в sqlRegPath.

Примечание: вы можете получить доступ к по умолчанию значение ключа (отображается инструментом командной строки REGEDT32.EXE как (Default)) если ValueName параметр в соответствующих функциях выше.

чтобы получить список разделы в разделе реестра используйте функцию GetRegKeyNamesили GetAllRegKeyNames. Этот список можно использовать для просмотра других разделов реестра.

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

получит все 32-битные и 64-битные ключи удаления.

обратите внимание на обработку null требуется в функциях, потому что SQL server может быть установить как 32 бит или 64 бит. Функции перегружены, поэтому вы все равно можете передать 32 - битный или 64-битный параметр, если это необходимо-однако, если вы его опустите, он попытается прочитать 64-битный, если это не удастся (нулевое значение), он читает 32-битные значения.

здесь есть одна специальность, потому что GetAllRegValueNames обычно используется в контексте цикла (см. пример выше), он возвращает пустой перечисляемый, а не null для упрощения foreach петли: если это не будет обработано, что таким образом, цикл должен быть с префиксом if заявление о проверке null что было бы громоздко делать это-так что это рассматривается один раз в функции.

зачем беспокоиться о null? потому что если вам все равно, у вас будет гораздо больше головных болей, выясняя, почему это исключение null reference было брошено в ваш код - вы потратили бы много времени, чтобы узнать, где и почему это произошло. И если это произошло на производстве, вы будете очень заняты изучение файлов журналов или журналов событий (я надеюсь, что вы реализовали ведение журнала) ... лучше избегать нулевых проблем, где вы можете обороняться. Операторы ?.,?[...] и ?? может помочь вам много (см. код выше). Есть хорошая статья, связанная с обсуждением нового nullable ссылочные типы В C#, который я рекомендую прочитать, а также этот об операторе Элвиса.


подсказка: вы можете используйте бесплатную версию Linqpad чтобы проверить все примеры под Windows. Он не требует установки. Не забудьте нажать F4 и вводим Microsoft.Win32 на вкладке импорт пространства имен. В Visual Studio требуется using Microsoft.Win32; в верхней части вашего кода.

Совет: чтобы ознакомиться с новой операторы обработки null, попробуйте (и отладьте) следующий код в 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>"
}

Если вам интересно,здесь вот несколько примеров, которые я собрал, показывая, что еще вы можете сделать с помощью инструмента.

У меня недостаточно репутации, чтобы комментировать, но стоит отметить, что это работает при открытии удаленного реестра с помощью OpenRemoteBaseKey. Добавление RegistryView.Параметр Registry64 позволяет 32-разрядной программе на машине A получить доступ к 64-разрядному реестру на машине B. прежде чем я передал этот параметр, моя программа читала 32-разрядную версию после OpenRemoteBaseKey и не нашла ключ, который мне нужен.

Примечание: В моем тесте удаленная машина была фактически моей машиной, но я получил к ней доступ через OpenRemoteBaseKey, так же, как и для другой машины.

попробуйте это (из 32-битного процесса):

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

(установлено, что здесь).

Если вы не можете использовать .NET 4 с его RegistryKey.OpenBaseKey(..., RegistryView.Registry64), вам нужно использовать Windows API напрямую.

минимальное взаимодействие выглядит так:

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

использовать его как:

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

Comments

    Ничего не найдено.