Доступ к реестру x64 из приложения x86

Автор Тема: Доступ к реестру x64 из приложения x86  (Прочитано 12020 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Доброго времени суток.

.NET 3.5 SP1

Мною ранее была написана некоторая библиотека AcadInfo.dll, скомпилированная как AnyCPU. В ней, помимо прочего, определён метод, считывающий из реестра необходимую информацию обо всех установленных на локальной\удалённой машине версиях программы AutoCAD (нередко их может быть установлено сразу несколько). Поскольку разрядность установленного AutoCAD всегда совпадает с разрядностью операционной системы, то вся необходимая информация размещена в ветке HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD. В ветке HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Autodesk подраздел AutoCAD отсутствует (можно убедиться через regedit).

Если пишется некое приложение, использующее AcadInfo.dll и оно так же компилируется как AnyCPU, то информация об установленных AutoCAD успешно считывается. Недавно обнаружил, что если это приложение компилировать как x86 и запустить его в операционной системе x64, то в коде библиотеки AcadInfo.dll (по прежнему скомпилированной как AnyCPU) значение свойства IntPtr.Size будет равным 4, а не 8 и получить ветки реестра, относящиеся к установленным AutoCAD не удаётся:
Код - C# [Выбрать]
  1. // parrentRegistry: HKEY_LOCAL_MACHINE
  2. // ParrentAcadRegistryKey: @"SOFTWARE\Autodesk\AutoCAD"
  3. regAcad = parrentRegistry.OpenSubKey(ParrentAcadRegistryKey, false); // null

Я попробовал воспользоваться методами расширений, приведённых в Example 3. Наименования этих методов некорректны и сбивают с толку, т. к. на самом деле они возвращают не ветку реестра, а строковое значение параметра, имя которого обозначено в качестве аргумента:
Код - C# [Выбрать]
  1. public static string GetRegKey64(this RegistryKey inKey, String inPropertyName)
  2. public static string GetRegKey32(this RegistryKey inKey, String inPropertyName)
т.е. по смыслу правильнее было бы как-то так:
Код - C# [Выбрать]
  1. public static string GetPropertyValueAsStringFromRegKey64(this RegistryKey inKey, String inPropertyName)
  2. public static string GetPropertyValueAsStringFromRegKey32(this RegistryKey inKey, String inPropertyName)

Однако код по указанной выше ссылке, использующий DllImport, в данном случае не работает так, как ожидалось (компилировал его как AnyCPU):
Код - C# [Выбрать]
  1. Int32 size = IntPtr.Size; // Если код данной библиотеки компилируется как AnyCPU,
  2. // но использующее её приложение скомпилировано как x64, то размер указателя
  3. // в коде этой библиотеки будет равен 4, даже если разрядность операционной
  4. // системы x64.
  5.  
  6. // parrentRegistry: HKEY_LOCAL_MACHINE
  7. // ParrentAcadRegistryKey: @"SOFTWARE\Autodesk\AutoCAD"
  8. regAcad = parrentRegistry.OpenSubKey(ParrentAcadRegistryKey, false); // null
  9. regAcad2 = parrentRegistry.OpenSubKey(@"SOFTWARE\Wow6432Node\Autodesk\AutoCAD", false); // null                
  10. String x64 = parrentRegistry.GetRegKey64(@"SOFTWARE\Autodesk\AutoCAD\R17.2\ACAD-7001:409\Location"); // null
  11. String x86 = parrentRegistry.GetRegKey32(@"SOFTWARE\Autodesk\AutoCAD\R17.2\ACAD-7001:409\Location"); // null
  12. String _x64 = parrentRegistry.GetRegKey64(@"SOFTWARE\Wow6432Node\Autodesk\AutoCAD\R17.2\ACAD-7001:409\Location"); // null
  13. String _x86 = parrentRegistry.GetRegKey32(@"SOFTWARE\Wow6432Node\Autodesk\AutoCAD\R17.2\ACAD-7001:409\Location"); // null

Как в обозначенной ситуации получить доступ к интересующим меня веткам реестра?

Спасибо.

Оффлайн bargool

  • ADN Club
  • ***
  • Сообщений: 111
  • Карма: 6
Андрей, я уже писал тебе, повторю здесь ссылку на решение этой проблемы: http://stackoverflow.com/questions/13728491/opensubkey-returns-null-for-a-registry-key-that-i-can-see-in-regedit-exe/13729137#13729137
Код - C# [Выбрать]
  1. using (var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
  2. using (var key = hklm.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"))
  3. {
  4.    // key now points to the 64-bit key
  5. }
Алексей

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Андрей, я уже писал тебе, повторю здесь ссылку
Ты невнимательно читаешь. Я же обозначил выше целевую платформу: .NET 3.5 SP1.  Код для .NET 4.0 в данном случае не подходит.

Оффлайн bargool

  • ADN Club
  • ***
  • Сообщений: 111
  • Карма: 6
Упс, я сейчас работаю под .net 4.0. Не знал, что в 3.5 этого нет.  :-X
Алексей

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Не знал, что в 3.5 этого нет. 
Странно, что не знал. Об этом ведь написано в указанной тобою выше ссылке :)

Оффлайн bargool

  • ADN Club
  • ***
  • Сообщений: 111
  • Карма: 6
Не знал, но забыл ))))
Алексей

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Пока что удалось получить значение строкового параметра:
Код - C# [Выбрать]
  1. // За основу взят код с сайта http://social.msdn.microsoft.com/Forums/ko-KR/da055767-0f69-4f07-b8e7-f3dce19f7ecb/windows-7-64bit-registry-access-using-x86-assembly?forum=netfx64bit
  2. using System;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ConsoleSample {
  7.  
  8.         public static class RegistryTools {
  9.  
  10.                 [System.Runtime.InteropServices.DllImport("advapi32.dll",
  11.                         CharSet = System.Runtime.InteropServices.CharSet.Unicode,
  12.                         EntryPoint = "RegQueryValueExW", SetLastError = true)]
  13.                 public static extern int RegQueryValueEx(UIntPtr hKey, string lpValueName,
  14.                         int lpReserved, out uint lpType, byte[] lpData, ref int lpcbData);
  15.  
  16.                 [System.Runtime.InteropServices.DllImport("advapi32.dll",
  17.                         CharSet = System.Runtime.InteropServices.CharSet.Unicode,
  18.                         EntryPoint = "RegOpenKeyExW", SetLastError = true)]
  19.                 public static extern int RegOpenKeyEx(UIntPtr hKey, string subKey,
  20.                         uint options, int sam, out UIntPtr phkResult);
  21.  
  22.                 public static UIntPtr HKEY_CURRENT_USER = (UIntPtr)0x80000001;
  23.                 public static UIntPtr HKEY_LOCAL_MACHINE = (UIntPtr)0x80000002;
  24.                 public static int KEY_QUERY_VALUE = 0x0001;
  25.                 public static int KEY_SET_VALUE = 0x0002;
  26.                 public static int KEY_CREATE_SUB_KEY = 0x0004;
  27.                 public static int KEY_ENUMERATE_SUB_KEYS = 0x0008;
  28.                 public static int KEY_WOW64_64KEY = 0x0100;
  29.                 public static int KEY_WOW64_32KEY = 0x0200;
  30.  
  31.                 /// <summary>
  32.                 /// Прочитать в реестре значение строкового параметра.
  33.                 /// </summary>
  34.                 /// <param name="rootRegKey">Куст. RegistryTools.HKEY_LOCAL_MACHINE
  35.                 /// или RegistryTools.HKEY_CURRENT_USER.</param>
  36.                 /// <param name="regKeyPath">Путь к разделу, в котором находится искомый параметр.
  37.                 /// В пути не указывается куст.</param>
  38.                 /// <param name="propName">Наименование искомого строкового параметра.</param>
  39.                 /// <returns>Возвращается значение параметра или null, если указанный параметр не найден.</returns>
  40.                 public static String GetStringPropertyValue(UIntPtr rootRegKey, String regKeyPath, String propName) {
  41.                         UIntPtr regKeyHandle;
  42.                         if (RegOpenKeyEx(rootRegKey, regKeyPath, 0,
  43.                                 KEY_QUERY_VALUE | KEY_WOW64_64KEY, out regKeyHandle) == 0) {
  44.                                 UInt32 type;
  45.                                 Int32 cbData = 2048;
  46.                                 Byte[] buf = new Byte[cbData];                         
  47.                                 if (RegQueryValueEx(regKeyHandle, propName, 0, out type, buf, ref cbData) == 0) {
  48.                                         Encoding encoding = Encoding.ASCII;
  49.                                         buf = Encoding.Convert(Encoding.Unicode, encoding, buf);
  50.                                         buf = buf.TakeWhile(n => n != 0).ToArray();
  51.                                         String text = encoding.GetString(buf, 0, buf.Length);
  52.                                         return text;
  53.                                 }
  54.                         }
  55.                         return null;
  56.                 }
  57.         }
  58. }

Работает так:
Код - C# [Выбрать]
  1. String location = RegistryTools.GetStringPropertyValue(RegistryTools.HKEY_LOCAL_MACHINE,
  2.                 @"SOFTWARE\Autodesk\AutoCAD\R17.2\ACAD-7001:409", "Location"); // C:\Program Files\Autodesk\AutoCAD 2009
  3.  

Однако это только капля из того, что требуется...

В идеале, мне бы хотелось получить экземпляр RegistryKey и работать с ним обычным образом, не изобретая велосипедов.
« Последнее редактирование: 20-02-2014, 17:46:56 от Андрей Бушман »

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Поскольку разрядность установленного AutoCAD всегда совпадает с разрядностью операционной системы
Утверждение не является истиной в последней инстанции. Например, AutoCAD 2008 Russian был только x86, тем не менее он прекрасно инсталлируется в Windows x64 как 32-разрядное приложение. Ряд вертикальных приложений и позднее были только x86, но при этом устанавливались в Windows x64 (даже в системных требованиях это было). При этом AutoCAD 2008 English был как x86, так и x64, и он действительно устанавливается в соответствии с разрядностью Windows.
В идеале, мне бы хотелось получить экземпляр RegistryKey и работать с ним обычным образом, не изобретая велосипед для каждой необходимой операции для каждого из существующих в реестре типов.
Мне кажется, что здесь приведен код, который должен тебя удовлетворить: http://dotnetgalactics.wordpress.com/2010/05/10/accessing-64-bit-registry-from-a-32-bit-process/
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Мне кажется, что здесь приведен код, который должен тебя удовлетворить: http://dotnetgalactics.wordpress.com/2010/05/10/accessing-64-bit-registry-from-a-32-bit-process/
Спасибо. По указанному коду есть некоторые странности...
Код - C# [Выбрать]
  1. RegistryKey key = RegistryTools._openSubKey(Registry.LocalMachine,
  2.          @"SOFTWARE\Autodesk\AutoCAD\R17.2\ACAD-7001:409", false, RegWow64Options.KEY_WOW64_64KEY);
  3. String[] names = key.GetSubKeyNames(); // здесь массив значений (правильно)
  4. String name = key.Name; // а здесь: "" (неправильно)
  5. RegistryKey subkey = key.OpenSubKey(names[0], false); // объект успешно создан
Пустое имя в экземпляре RegistryKey... Хотя, в принципе, это не смертельно, т.к. значение этого имени мне и так известно, поскольку оно мною же и было передано в качестве параметра в _openSubKey.

Спасибо.
« Последнее редактирование: 20-02-2014, 18:42:33 от Андрей Бушман »

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Такой ещё нюанс: как грамотно определить, что разрядность операционной системы не совпадает с разрядностью приложения, в котором выполняется текущий код? Размер указателя (IntPtr.Size) показывает разрядность текущего приложения, даже если код сейчас выполняется в библиотеке, скомпилированной как AnyCPU и затем подключенной к этому приложению. В .Net 4.0 есть специальное свойство Environment.Is64BitOperatingSystem, но как это грамотно выяснить в .NET 3.5 SP1?

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Доступ к реестру x64 из приложения x86
« Ответ #10 : 20-02-2014, 18:44:01 »
1. Переменная окружения %PROCESSOR_ARCHITECTURE%
2. Еще: http://support.microsoft.com/kb/556009/ru
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Доступ к реестру x64 из приложения x86
« Ответ #11 : 20-02-2014, 18:48:36 »
Спасибо

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Доступ к реестру x64 из приложения x86
« Ответ #12 : 20-02-2014, 22:05:48 »
Утверждение не является истиной в последней инстанции. Например, AutoCAD 2008 Russian был только x86, тем не менее он прекрасно инсталлируется в Windows x64 как 32-разрядное приложение. Ряд вертикальных приложений и позднее были только x86, но при этом устанавливались в Windows x64 (даже в системных требованиях это было). При этом AutoCAD 2008 English был как x86, так и x64, и он действительно устанавливается в соответствии с разрядностью Windows.
Я думал, что до AutoCAD 2009 выпускались только сборки x86 и не знал о существовании AutoCAD 2008 English x64.

Так же был уверен в том, что начиная с 2009-го разрядности устанавливаемого AutoCAD соответствуют разрядности операционной системы, т.к. во первых - в исходном дистрибутиве AutoCAD присутствуют две версии установщика: x86 и x64, а во вторых - в процессе написания AcadInfo.dll я пробовал принудительно устанавливать AutoCAD x86 на операционную систему x64, чтобы узнать, стоит ли мне обрабатывать ситуацию, когда AutoCAD x86 установлен в систему x64: инсталлятор отказался устанавливать AutoCAD x86 на систему x64.

В виду этого я сделал вывод, что начиная с версии 2009, разрядность AutoCAD должна соответствовать разрядности операционной системы. Я не проверял вертикальные приложения, ограничившись только обычным AutoCAD.

Касательно вертикалок (меня, в первую очередь, интересуют версии не старее 2009-й):
1. Какие именно версии AutoCAD (вертикалки) устанавливаются на операционку иной разрядности?
2. С какой версии AutoCAD разрядности стали однозначно соответствовать (если это окончательно произошло)?

Спасибо за информацию, значит учту это, добавив в код недостающую обработку для случая AutoCAD x86 на Windows x64.
« Последнее редактирование: 20-02-2014, 22:35:40 от Андрей Бушман »

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Доступ к реестру x64 из приложения x86
« Ответ #13 : 21-02-2014, 01:18:03 »
1. Какие именно версии AutoCAD (вертикалки) устанавливаются на операционку иной разрядности?
Точно помню, что Civil 3D 2009 был только x86. Про остальные продукты сейчас сказать не могу, так как на сайте ADN остались только последние три версии. Они (насколько я смог посмотреть) в двух разрядностях. Т.е. официальным способом установить AutoCAD x86 на Windows x64 нельзя. Но это не значит, что это сделать нельзя вообще. Даже когда-то был официальный документ от Autodesk о том как AutoCAD x86 непомню какой-то версии устанавливать на Windows x64.  Это решалось легкой правкой msi-файла при помощи ORCA MSI Editor.
2. С какой версии AutoCAD разрядности стали однозначно соответствовать (если это окончательно произошло)?
Затрудняюсь сказать точно. Вроде бы с 2010, но 100% гарантии дать не могу.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Доступ к реестру x64 из приложения x86
« Ответ #14 : 21-02-2014, 15:09:33 »
1. Переменная окружения %PROCESSOR_ARCHITECTURE%
2. Еще: http://support.microsoft.com/kb/556009/ru
Второй вариант корректен, а первый - нет, т.к. для приложения x86, запущенного в операционной системе x64, эта переменная возвращает значение x86.

Итоговая обобщённая информация по обозначенным в данной теме вопросам опубликована мною в блоге.
« Последнее редактирование: 21-02-2014, 17:27:12 от Андрей Бушман »

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Доступ к реестру x64 из приложения x86
« Ответ #15 : 24-02-2014, 11:38:40 »
2. Еще: http://support.microsoft.com/kb/556009/ru
Хм... Как выяснилось, числовое значение ключа реестра (Platform ID) у меня содержит одно и то же значение что в Windows 7 x64, что в Windows XP SP3 x86: 0x01.
Отличаются только текстовое сообщение идентификатора процессора (ключ Identifier):

- Intel64 Family 6 Model 23 Stepping 10
- x86 Family 6 Model 26 Stepping 5


Согласно MSDN:
Цитировать
The above “x86” and “0x00000020(32)” indicate that the Operating System version is 32 bit.
Вот только никакого “0x00000020(32)” в x86 (виртуальная машинка VMWare) у меня нет, как выше мною уже было обозначено.

Причём в операционной системе x86 код
Код - C# [Выбрать]
  1. regAcad = parrentRegistry.OpenSubKey(ParrentAcadRegistryKey,
  2.         false, RegWow64Options.KEY_WOW64_64KEY);
возвращается не null (как я ожидал), а RegistryKey тот же, что и для
Код - C# [Выбрать]
  1. regAcad = parrentRegistry.OpenSubKey(ParrentAcadRegistryKey,
  2.         false, RegWow64Options.KEY_WOW64_32KEY);
Т.о. в операционной системе x86 одна и та же информация извлекается дважды (что так же не есть гуд).

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Доступ к реестру x64 из приложения x86
« Ответ #16 : 24-02-2014, 12:26:06 »
Переписал код следующим образом:
Код - C# [Выбрать]
  1. /// <summary>
  2. /// Проверка на то, является ли текущий операционная система 64-битной.
  3. /// </summary>
  4. /// <returns>true - текущая операционная система x64, иначе - false.</returns>
  5. public static Boolean Is64BitOS() {
  6.         Boolean isWow64 = false;
  7.         if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion
  8.                 .Version.Minor >= 1) || Environment.OSVersion.Version.Major >= 6) {
  9.                 using (Process p = Process.GetCurrentProcess()) {
  10.                         Boolean retVal;
  11.                         if (!IsWow64Process(p.Handle, out retVal)) {
  12.                                 isWow64 = false;
  13.                         }
  14.                         isWow64 = retVal;
  15.                 }
  16.         }
  17.         else {
  18.                 isWow64 = false;
  19.         }
  20.  
  21.         Boolean is64BitProcess = (IntPtr.Size == 8);
  22.         Boolean is64BitOperatingSystem = is64BitProcess || isWow64;
  23.         return is64BitOperatingSystem;
  24. }
Корректно определяет разрядность операционной системы как в Windows 7 x64, так и в Windows XP SP3 x86, не зависимо от того, как откомпилирован код (x86, x64, AnyCPU).