Читання 64-бітного реєстру з 32-бітної програми


98

У мене є проект # unit unit test, який складений для AnyCPU. Наш сервер збірки - це 64-бітна машина, на якій встановлено 64-бітний екземпляр SQL Express.

Тестовий проект використовує код, подібний до наведеного нижче, для визначення шляху до файлів .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();
    }

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

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

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

Чи є спосіб для програми, що працює в 32-бітному режимі, отримати доступ до реєстру за межами Wow6432Node?

Відповіді:


21

вам потрібно використовувати параметр KEY_WOW64_64KEY під час створення / відкриття ключа реєстру. Але AFAIK це неможливо з класом реєстру, а лише при безпосередньому використанні API.

Це може допомогти вам розпочати.


151

Існує ще нативная підтримка доступу реєстру в 64 - розрядної ОС Windows з використанням .NET Framework 4.x . Наступний код тестується з   Windows 7, 64 біт,   а також з   Windows 10, 64 біт .

Замість використання "Wow6432Node", яке емулює вузол, перекладаючи одне дерево реєстру в інше, роблячи його віртуальним, ви можете виконати наступне:

Вирішіть, чи потрібен вам доступ до 64-розрядного або 32-розрядного реєстру, і використовуйте його, як описано нижче. Ви також можете використовувати код, про який я згадав пізніше (розділ "Додаткова інформація"), який створює запит об'єднання для отримання ключів реєстру від обох вузлів в одному запиті - так що ви все одно можете запитувати їх, використовуючи їх реальний шлях.

64-розрядний реєстр

Щоб отримати доступ до 64-розрядного реєстру , ви можете використовувати RegistryView.Registry64наступне:

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

32-розрядний реєстр

Якщо ви хочете отримати доступ до 32-бітного реєстру , використовуйте RegistryView.Registry32наступне:

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-бітну версію для другого параметра ( RegistryView.Registry64проти RegistryView.Registry32).

Зауважте, що

  • У 64- HKEY_LOCAL_MACHINE\Software\Wow6432Nodeбітовій Windows містить значення, що використовуються 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");

Додаткова інформація - для практичного використання:

Я хотів би додати цікавий підхід , запропонований Джоні Сковдалем у коментарях, який я підібрав для розробки деяких корисних функцій, використовуючи його підхід: У деяких ситуаціях ви хочете повернути всі ключі, незалежно від того, чи є вони 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);
}

Тепер ви можете просто використовувати наведені вище функції наступним чином:

Приклад 1: Отримайте імена екземплярів 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}");
}

надасть вам список імен значень та значень у sqlRegPath.

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

Щоб отримати список підключів у розділі реєстру, використовуйте функцію GetRegKeyNamesабо GetAllRegKeyNames. Ви можете використовувати цей список для перегляду подальших ключів у реєстрі.

Приклад 2: Отримайте інформацію про видалення встановленого програмного забезпечення

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

отримає всі 32-розрядні та 64-розрядні ключі видалення.

Зверніть увагу на необхідну обробку функцій, оскільки SQL-сервер можна встановити як 32-розрядний або як 64-розрядний (Приклад 1 вище). Функції перевантажені, тому ви все одно можете передавати 32-бітний або 64-бітний параметр, якщо потрібно - однак, якщо його опустити, він спробує прочитати 64-бітний, якщо це не вдається (нульове значення), він зчитує 32-бітні значення.

Тут є одна спеціальність: оскільки GetAllRegValueNamesзазвичай використовується в контексті циклу (див. Приклад 1 вище), він повертає порожнє перелічуване, а не nullдля спрощення foreachциклів: якщо він не буде оброблятися таким чином, цикл повинен мати префікс ifперевірка особистих даних для nullяких буде обтяжливою необхідності зробити це - так , що розглядається один раз в функції.

Чому турбуватися про нуль? Тому що якщо вас не хвилює, у вас буде набагато більше головних болів, з'ясувавши, чому цей нульовий винятковий виняток був кинутий у ваш код - ви витратите багато часу, щоб дізнатися, де і чому це сталося. І якщо це сталося у виробництві, ви будете дуже зайняті вивченням файлів журналів або журналів подій (сподіваюсь, у вас введено ведення журналу) ... краще уникайте нульових проблем, де ви можете захисним способом. Оператори ?., ?[... ]і ??можуть вам дуже допомогти (див. Наведений вище код). Є приємна пов’язана стаття, де обговорюються нові типи посилань , що дозволяють обнулятись, в C # , яку я рекомендую прочитати, а також ця про оператор Елвіса.


Підказка: Ви можете використовувати безкоштовну версію Linqpad для тестування всіх прикладів під Windows. Це не вимагає установки. Не забудьте натиснути F4та ввести Microsoft.Win32на вкладці імпорту простору імен. У Visual Studio потрібно using Microsoft.Win32;вгорі коду.

Порада. Щоб ознайомитися з новими операторами обробки нуля , спробуйте (та налагодьте) такий код у LinqPad:

Приклад 3: Демонстрація операторів обробки нуля

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

Спробуйте за допомогою .Net fiddle

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


2
Дякую за цю вичерпну відповідь. З пам’яті я думаю, що використовував .NET 3.5, коли публікував запитання, але приємно бачити, що .NET 4 покращив ситуацію
Девід Гардінер

2
Ласкаво просимо. Нещодавно у мене була подібна проблема з 64-бітовим реєстром, яку я вже вирішив, тому я вважав, що варто поділитися рішенням.
Matt

2
Це саме те, що я шукав. Я роблю це в Windows 9.1, і це чудово працює.
Michiel Bugher

1
@AZ_ - дякую за редагування, ти маєш рацію, ключ потрібно закрити!
Метт

1
@JohnySkovdal - Я змінив заголовок, щоб пояснити, що я просто надаю додаткову (необов’язкову) інформацію - для тих, хто хоче глибше заглибитися в цю справу.
Метт

6

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

Примітка. У моєму тесті віддалена машина фактично була моєю машиною, але я отримав доступ до неї через OpenRemoteBaseKey, як і для іншої машини.


4

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

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

(знайшов, що тут ).


1
Приємний підказка, вона дозволяє маніпулювати реєстром в пакетному режимі. Використовуйте, reg.exe /?щоб отримати докладнішу інформацію ...
Метт,

4

Якщо ви не можете використовувати .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);
    }
}

0

З прочитаного та з власних тестів мені здається, що реєстр слід перевіряти за цим шляхом "ПРОГРАМНЕ ЗАБЕЗПЕЧЕННЯ \ Microsoft \ Windows \ CurrentVersion \ Uninstall". Оскільки в інших шляхах реєстри не видаляються після видалення програми.

Таким чином я отримав 64 регістри з 32-бітною конфігурацією.

string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
if (key != null)
{
    var list = key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName).GetValue("DisplayName")).ToList();

    key.Close();
}

Для 32 реєстрів є:

registryKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.