Когда-то здесь я выкладывал инструменты по работе с реестром для .NET 3.5 SP1. Обнаружилось, что начиная с .NET 4.0 сигнатура нужных для обозначенного кода конструкторов класса RegistryKey была изменена. Как следствие - если наша сборка, скомпилированная под .NET 3.5 SP1 в дальнейшем окажется загруженной в .NET 4.0 (или любую более новую), то мы будем получать исключение времени выполнения при вызове некоторых методов опубликованного кода.
Я внёс некоторые правки в исходный код класса RegistryExtensions и написал несколько интеграционных тестов, проверяющих корректность работы кода в различных ситуациях (в т.ч. избавился от всех директив препроцессора - теперь они не нужны). Проверял в .NET 3.5 SP1, 4.0 и 4.6.1 для платформ x86|x64|AnyCPU:
Обозначенный код я поддерживаю в виду того, что у меня имеется в наработке некоторый объем кода, компилируемого под .NET 3.5 SP1 (т.к. AutoCAD 2009 не поддерживает более новые версии .Net Framework). Этот код должен без проблем компилироваться и успешно работать не только в .NET 3.5 SP1, но и во всех более новых версиях .NET. Кроме того, необходимо, чтобы ранее скомпилированные мною под .NET 3.5 SP1 сборки успешно загружались и работали в .NET 4.0 - 4.6.1.
Итак, ниже - обновлённый вариант кода, устраняющий проблему, упомянутую в начале заметки. Сразу за ним размещён исходный код интеграционных тестов. Пришлось воспользоваться интеграционными тестами, т.к. модульные применительно к данной задаче не подойдут.
======================================================================
/* AcProducts
* RegistryExtensions.cs
* © Андрей Бушман, 2014
*
* В файле RegistryExtensions.cs определён дополнительный
* функционал, расширяющий возможности работы с реестром из
* .NET приложений, написанных на .NET 3.5 SP1.
*
* ИЗМЕНЕНИЯ:
* 10-авг-2016
* В код внесены правки для того, чтобы он мог
* использоваться не только в .NET 3.5 SP1, но и во всех
* более новых версиях .NET Framework: например, когда
* сборка, скомпилированная под .NET 3.5 SP1 загружается в
* код .Net 4.6.1. Код не зависит от разрядности (x86|x64|
* AnyCPU).
*/
using System;
using Microsoft.Win32;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Bushman.AcProducts {
/// <summary>
/// Данный класс предназначен для предоставления 32-битным
/// приложениям доступа к 64-битным разделам реестра. Класс
/// так же может использоваться для предоставления
/// 64-битным приложениям ветке реестра, предназначенной
/// для 32-битных. За основу взят код, опубликованный в
/// блоге http://clck.ru/96A9U
/// </summary>
public static class RegistryExtensions {
/// <summary>
/// Открытие ключа реестра, с указанием того, какую
/// именно часть следует открывать: записи для
/// 32-битных приложений, или же записи для 64-битных.
///
/// ВНИМАНИЕ
/// У данного метода имеется побочный эффект: свойство
/// `Name` у возвращаемого объекта `RegistryKey` даёт
/// пустую строку. Однако это не страшно, т.к. имя нам
/// известно - первую часть можно получить из родителя,
/// а вторую часть этого имени мы и так знаем,
/// поскольку передаём её в виде параметра.
/// </summary>
/// <param name="parentKey">Родительский элемент
/// RegistryKey, в котором следует выполнить открытие
/// подраздера.</param>
/// <param name="subKeyName">Name of the key to be
/// opened</param>
/// <param name="writable">true - открывать для чтения
/// и записи; false - открывать только для чтения.
/// </param>
/// <param name="options">Какую именно часть реестра
/// следует открывать: относящуюся к 32-битным
/// приложениям или же относящуюся к 64-битным.
/// </param>
/// <returns>Возвращается RegistryKey или null, если по
/// каким-либо причинам получить RegistryKey не
/// удалось.</returns>
public static RegistryKey OpenSubKey(this RegistryKey
parentKey, String subKeyName, Boolean writable,
RegWow64Options options) {
// Проверка работоспособности
if (parentKey == null || GetPtr(
parentKey) == IntPtr.Zero) {
return null;
}
// Назначение прав
Int32 rights = (Int32) (writable ? RegistryRights
.WriteKey : RegistryRights.ReadKey);
// Вызов функций неуправляемого кода
Int32 subKeyHandle, result = RegOpenKeyEx(
GetPtr(parentKey), subKeyName, 0,
rights | (Int32) options, out subKeyHandle);
// Если мы ошиблись - возвращаем null
if (result != 0) {
return null;
}
/* Получаем ключ, представленный указателем,
* возвращённым из RegOpenKeyEx */
RegistryKey subKey = PtrToRegistryKey((IntPtr)
subKeyHandle, writable, false, options);
return subKey;
}
/// <summary>
/// Получить указатель на ключ реестра.
/// </summary>
/// <param name="registryKey">Ключ реестра, указатель
/// на который нужно получить.
/// </param>
/// <returns>Возвращается объект IntPtr. Если не
/// удалось получить указатель на обозначенный объект
/// RegistryKey, то возвращается IntPtr.Zero.</returns>
public static IntPtr GetPtr(this RegistryKey
registryKey) {
if (registryKey == null)
return IntPtr.Zero;
/* The `RegistryKey.Handle` property appears since
* .Net 4.0, therefore for .Net 3.5 I get it
* through reflection. */
Type registryKeyType = typeof(RegistryKey);
FieldInfo fieldInfo = registryKeyType.GetField(
"hkey", BindingFlags.NonPublic | BindingFlags
.Instance);
SafeHandle handle = (SafeHandle) fieldInfo.GetValue
(registryKey);
IntPtr unsafeHandle = handle.DangerousGetHandle(
);
return unsafeHandle;
}
/// <summary>
/// Получить ключ реестра на основе его указателя.
/// </summary>
/// <param name="hKey">Указатель на ключ реестра
/// </param>
/// <param name="writable">true - открыть для записи;
/// false - для чтения.</param>
/// <param name="ownsHandle">Владеем ли мы
/// дескриптором: true - да, false - нет.</param>
/// <returns>Возвращается объект RegistryKey,
/// соответствующий полученному указателю.</returns>
public static RegistryKey PtrToRegistryKey(IntPtr
hKey, Boolean writable, Boolean ownsHandle,
RegWow64Options opt) {
if (IntPtr.Zero == hKey) {
return null;
}
Type safeRegistryHandleType =
typeof(SafeHandleZeroOrMinusOneIsInvalid)
.Assembly.GetType("Microsoft.Win32." +
"SafeHandles.SafeRegistryHandle");
/* Получаем массив типов, соответствующих
* аргументом конструктора, который нам нужен. */
Type[] argTypes = new Type[] { typeof(IntPtr),
typeof(Boolean) };
BindingFlags flags = default(BindingFlags);
if (Environment.Version.Major < 4) {
flags = BindingFlags.Instance | BindingFlags
.NonPublic;
}
else {
flags = BindingFlags.Instance | BindingFlags
.Public;
}
// Получаем ConstructorInfo для нашего объекта
ConstructorInfo safeRegistryHandleCtorInfo =
safeRegistryHandleType.GetConstructor(flags,
null, argTypes, null);
/* Вызываем конструктор для SafeRegistryHandle.
* Класс SafeRegistryHandle появился начиная с .NET
* 4.0. */
Object safeHandle = safeRegistryHandleCtorInfo
.Invoke(new Object[] { hKey, ownsHandle });
Type registryKeyType = typeof(RegistryKey);
Type registryViewType = null;
/*Получаем массив типов, соответствующих аргументом
* конструктора, который нам нужен */
Type[] registryKeyConstructorTypes = null;
if (Environment.Version.Major < 4) {
registryKeyConstructorTypes = new Type[] {
safeRegistryHandleType, typeof(bool) };
}
else {
registryViewType = typeof(
SafeHandleZeroOrMinusOneIsInvalid).Assembly
.GetType("Microsoft.Win32.RegistryView");
registryKeyConstructorTypes = new Type[] {
safeRegistryHandleType, typeof(bool),
registryViewType };
flags = BindingFlags.Instance | BindingFlags
.NonPublic;
}
// Получаем ConstructorInfo для нашего объекта
ConstructorInfo registryKeyCtorInfo =
registryKeyType.GetConstructor(flags, null,
registryKeyConstructorTypes, null);
RegistryKey resultKey = null;
if (Environment.Version.Major < 4) {
// Вызываем конструктор для RegistryKey
resultKey = (RegistryKey) registryKeyCtorInfo
.Invoke(new Object[] {
safeHandle, writable });
}
else {
// Вызываем конструктор для RegistryKey
resultKey = (RegistryKey) registryKeyCtorInfo
.Invoke(new Object[] {
safeHandle, writable, (int) opt});
}
// возвращаем полученный ключ реестра
return resultKey;
}
/// <summary>
/// Получение числового значения указателя на искомый
/// подраздел реестра.
/// </summary>
/// <param name="hKey">Указатель на родительский раздел
/// реестра.</param>
/// <param name="subKey">Имя искомого подраздела.
/// </param>
/// <param name="ulOptions">Этот параметр
/// зарезервирован и всегда должен быть равным 0.
/// </param>
/// <param name="samDesired">Права доступа (чтение\
/// запись) и указание того, как именно следует
/// открывать реестр. Значение этого параметра
/// формируется путём применения операции логического
/// "И" для объектов перечислений RegistryRights и
/// RegWow64Options.</param>
/// <param name="phkResult">Ссылка на переменную, в
/// которую следует сохранить полученное числовое
/// значение указателя на искомый подраздел.</param>
/// <returns>В случае успеха возвращается 0.</returns>
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
static extern Int32 RegOpenKeyEx(IntPtr hKey,
String subKey, Int32 ulOptions, Int32 samDesired,
out Int32 phkResult);
}
/// <summary>
/// Перечисление указывает, какую именно часть реестра
/// следует открывать: относящуюся к 32-битным приложениям
/// или же относящуюся к 64-битным.
/// </summary>
public enum RegWow64Options {
/// <summary>
/// Открывать ту часть реестра, которая хранит
/// информацию приложений, разрядность которых
/// соответствует разрядности текущего приложения
/// (x86\x64).</summary>
None = 0,
/// <summary>
/// Открывать часть реестра, относящуюся к 64-битным
/// приложениям.
/// </summary>
KEY_WOW64_64KEY = 0x0100,
/// <summary>
/// Открывать часть реестра, относящуюся к 32-битным
/// приложениям.
/// </summary>
KEY_WOW64_32KEY = 0x0200
}
/// <summary>
/// Перечисление, указывающее на то, с каким уровнем
/// доступа следует открывать ветку реестра: для чтения,
/// или же для чтения\записи.
/// </summary>
public enum RegistryRights {
/// <summary>
/// Открыть только для чтения.
/// </summary>
ReadKey = 131097,
/// <summary>
/// Открыть для чтения и записи.
/// </summary>
WriteKey = 131078
}
}
* RegistryExtensions.cs
* © Андрей Бушман, 2014
*
* В файле RegistryExtensions.cs определён дополнительный
* функционал, расширяющий возможности работы с реестром из
* .NET приложений, написанных на .NET 3.5 SP1.
*
* ИЗМЕНЕНИЯ:
* 10-авг-2016
* В код внесены правки для того, чтобы он мог
* использоваться не только в .NET 3.5 SP1, но и во всех
* более новых версиях .NET Framework: например, когда
* сборка, скомпилированная под .NET 3.5 SP1 загружается в
* код .Net 4.6.1. Код не зависит от разрядности (x86|x64|
* AnyCPU).
*/
using System;
using Microsoft.Win32;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Bushman.AcProducts {
/// <summary>
/// Данный класс предназначен для предоставления 32-битным
/// приложениям доступа к 64-битным разделам реестра. Класс
/// так же может использоваться для предоставления
/// 64-битным приложениям ветке реестра, предназначенной
/// для 32-битных. За основу взят код, опубликованный в
/// блоге http://clck.ru/96A9U
/// </summary>
public static class RegistryExtensions {
/// <summary>
/// Открытие ключа реестра, с указанием того, какую
/// именно часть следует открывать: записи для
/// 32-битных приложений, или же записи для 64-битных.
///
/// ВНИМАНИЕ
/// У данного метода имеется побочный эффект: свойство
/// `Name` у возвращаемого объекта `RegistryKey` даёт
/// пустую строку. Однако это не страшно, т.к. имя нам
/// известно - первую часть можно получить из родителя,
/// а вторую часть этого имени мы и так знаем,
/// поскольку передаём её в виде параметра.
/// </summary>
/// <param name="parentKey">Родительский элемент
/// RegistryKey, в котором следует выполнить открытие
/// подраздера.</param>
/// <param name="subKeyName">Name of the key to be
/// opened</param>
/// <param name="writable">true - открывать для чтения
/// и записи; false - открывать только для чтения.
/// </param>
/// <param name="options">Какую именно часть реестра
/// следует открывать: относящуюся к 32-битным
/// приложениям или же относящуюся к 64-битным.
/// </param>
/// <returns>Возвращается RegistryKey или null, если по
/// каким-либо причинам получить RegistryKey не
/// удалось.</returns>
public static RegistryKey OpenSubKey(this RegistryKey
parentKey, String subKeyName, Boolean writable,
RegWow64Options options) {
// Проверка работоспособности
if (parentKey == null || GetPtr(
parentKey) == IntPtr.Zero) {
return null;
}
// Назначение прав
Int32 rights = (Int32) (writable ? RegistryRights
.WriteKey : RegistryRights.ReadKey);
// Вызов функций неуправляемого кода
Int32 subKeyHandle, result = RegOpenKeyEx(
GetPtr(parentKey), subKeyName, 0,
rights | (Int32) options, out subKeyHandle);
// Если мы ошиблись - возвращаем null
if (result != 0) {
return null;
}
/* Получаем ключ, представленный указателем,
* возвращённым из RegOpenKeyEx */
RegistryKey subKey = PtrToRegistryKey((IntPtr)
subKeyHandle, writable, false, options);
return subKey;
}
/// <summary>
/// Получить указатель на ключ реестра.
/// </summary>
/// <param name="registryKey">Ключ реестра, указатель
/// на который нужно получить.
/// </param>
/// <returns>Возвращается объект IntPtr. Если не
/// удалось получить указатель на обозначенный объект
/// RegistryKey, то возвращается IntPtr.Zero.</returns>
public static IntPtr GetPtr(this RegistryKey
registryKey) {
if (registryKey == null)
return IntPtr.Zero;
/* The `RegistryKey.Handle` property appears since
* .Net 4.0, therefore for .Net 3.5 I get it
* through reflection. */
Type registryKeyType = typeof(RegistryKey);
FieldInfo fieldInfo = registryKeyType.GetField(
"hkey", BindingFlags.NonPublic | BindingFlags
.Instance);
SafeHandle handle = (SafeHandle) fieldInfo.GetValue
(registryKey);
IntPtr unsafeHandle = handle.DangerousGetHandle(
);
return unsafeHandle;
}
/// <summary>
/// Получить ключ реестра на основе его указателя.
/// </summary>
/// <param name="hKey">Указатель на ключ реестра
/// </param>
/// <param name="writable">true - открыть для записи;
/// false - для чтения.</param>
/// <param name="ownsHandle">Владеем ли мы
/// дескриптором: true - да, false - нет.</param>
/// <returns>Возвращается объект RegistryKey,
/// соответствующий полученному указателю.</returns>
public static RegistryKey PtrToRegistryKey(IntPtr
hKey, Boolean writable, Boolean ownsHandle,
RegWow64Options opt) {
if (IntPtr.Zero == hKey) {
return null;
}
Type safeRegistryHandleType =
typeof(SafeHandleZeroOrMinusOneIsInvalid)
.Assembly.GetType("Microsoft.Win32." +
"SafeHandles.SafeRegistryHandle");
/* Получаем массив типов, соответствующих
* аргументом конструктора, который нам нужен. */
Type[] argTypes = new Type[] { typeof(IntPtr),
typeof(Boolean) };
BindingFlags flags = default(BindingFlags);
if (Environment.Version.Major < 4) {
flags = BindingFlags.Instance | BindingFlags
.NonPublic;
}
else {
flags = BindingFlags.Instance | BindingFlags
.Public;
}
// Получаем ConstructorInfo для нашего объекта
ConstructorInfo safeRegistryHandleCtorInfo =
safeRegistryHandleType.GetConstructor(flags,
null, argTypes, null);
/* Вызываем конструктор для SafeRegistryHandle.
* Класс SafeRegistryHandle появился начиная с .NET
* 4.0. */
Object safeHandle = safeRegistryHandleCtorInfo
.Invoke(new Object[] { hKey, ownsHandle });
Type registryKeyType = typeof(RegistryKey);
Type registryViewType = null;
/*Получаем массив типов, соответствующих аргументом
* конструктора, который нам нужен */
Type[] registryKeyConstructorTypes = null;
if (Environment.Version.Major < 4) {
registryKeyConstructorTypes = new Type[] {
safeRegistryHandleType, typeof(bool) };
}
else {
registryViewType = typeof(
SafeHandleZeroOrMinusOneIsInvalid).Assembly
.GetType("Microsoft.Win32.RegistryView");
registryKeyConstructorTypes = new Type[] {
safeRegistryHandleType, typeof(bool),
registryViewType };
flags = BindingFlags.Instance | BindingFlags
.NonPublic;
}
// Получаем ConstructorInfo для нашего объекта
ConstructorInfo registryKeyCtorInfo =
registryKeyType.GetConstructor(flags, null,
registryKeyConstructorTypes, null);
RegistryKey resultKey = null;
if (Environment.Version.Major < 4) {
// Вызываем конструктор для RegistryKey
resultKey = (RegistryKey) registryKeyCtorInfo
.Invoke(new Object[] {
safeHandle, writable });
}
else {
// Вызываем конструктор для RegistryKey
resultKey = (RegistryKey) registryKeyCtorInfo
.Invoke(new Object[] {
safeHandle, writable, (int) opt});
}
// возвращаем полученный ключ реестра
return resultKey;
}
/// <summary>
/// Получение числового значения указателя на искомый
/// подраздел реестра.
/// </summary>
/// <param name="hKey">Указатель на родительский раздел
/// реестра.</param>
/// <param name="subKey">Имя искомого подраздела.
/// </param>
/// <param name="ulOptions">Этот параметр
/// зарезервирован и всегда должен быть равным 0.
/// </param>
/// <param name="samDesired">Права доступа (чтение\
/// запись) и указание того, как именно следует
/// открывать реестр. Значение этого параметра
/// формируется путём применения операции логического
/// "И" для объектов перечислений RegistryRights и
/// RegWow64Options.</param>
/// <param name="phkResult">Ссылка на переменную, в
/// которую следует сохранить полученное числовое
/// значение указателя на искомый подраздел.</param>
/// <returns>В случае успеха возвращается 0.</returns>
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
static extern Int32 RegOpenKeyEx(IntPtr hKey,
String subKey, Int32 ulOptions, Int32 samDesired,
out Int32 phkResult);
}
/// <summary>
/// Перечисление указывает, какую именно часть реестра
/// следует открывать: относящуюся к 32-битным приложениям
/// или же относящуюся к 64-битным.
/// </summary>
public enum RegWow64Options {
/// <summary>
/// Открывать ту часть реестра, которая хранит
/// информацию приложений, разрядность которых
/// соответствует разрядности текущего приложения
/// (x86\x64).</summary>
None = 0,
/// <summary>
/// Открывать часть реестра, относящуюся к 64-битным
/// приложениям.
/// </summary>
KEY_WOW64_64KEY = 0x0100,
/// <summary>
/// Открывать часть реестра, относящуюся к 32-битным
/// приложениям.
/// </summary>
KEY_WOW64_32KEY = 0x0200
}
/// <summary>
/// Перечисление, указывающее на то, с каким уровнем
/// доступа следует открывать ветку реестра: для чтения,
/// или же для чтения\записи.
/// </summary>
public enum RegistryRights {
/// <summary>
/// Открыть только для чтения.
/// </summary>
ReadKey = 131097,
/// <summary>
/// Открыть для чтения и записи.
/// </summary>
WriteKey = 131078
}
}
==================================================================
Интеграционные тесты:
==================================================================
/* AcProducts.IntegrationTests
* RegistryExtensionsTests.cs
* © Andrey Bushman, 2016
*
* Integration tests of Bushman.AcProducts.RegistryExtensions
* class.
*/
using System;
using Microsoft.Win32;
using NUnit.Framework;
namespace Bushman.AcProducts.IntegrationTests {
[TestFixture]
public class RegistryExtensionsTests {
[Test]
public void GetPtr_Returns_ValidPtr() {
// `Registry.LocalMachine` key always exists.
RegistryKey rk = Registry.LocalMachine;
IntPtr rkPtr = rk.Handle.DangerousGetHandle();
/* The `RegistryKey.Handle` property appears since
* .Net 4.0, therefore for .Net 3.5 in the code
* under test I get it through reflection. */
IntPtr rkPtr2 = RegistryExtensions.GetPtr(rk);
Assert.AreEqual(rkPtr, rkPtr2);
}
[Test]
public void GetPtr_ReturnsZero_ForNull() {
/* The `RegistryKey.Handle` property appears since
* .Net 4.0, therefore for .Net 3.5 in the code
* under test I get it through reflection. */
IntPtr ptr = RegistryExtensions.GetPtr(null);
Assert.AreEqual(IntPtr.Zero, ptr);
}
[TestCase(@"SOFTWARE\7-Zip", RegWow64Options
.KEY_WOW64_64KEY, false)]
[TestCase(@"SOFTWARE\7-Zip", RegWow64Options
.KEY_WOW64_32KEY, true)]
[TestCase(@"SOFTWARE\Notepad++",
RegWow64Options.KEY_WOW64_64KEY, true)]
[TestCase(@"SOFTWARE\Notepad++",
RegWow64Options.KEY_WOW64_32KEY, false)]
[Description("7-Zip and Notepad++ are to be installed"
)]
public void OpenSubKey_ReturnsValidValue(string subkey,
RegWow64Options opt, bool isNull) {
RegistryKey rk = Registry.LocalMachine;
RegistryKey rk2 = RegistryExtensions.OpenSubKey(rk,
subkey, false, opt);
if (isNull) {
Assert.IsNull(rk2);
}
else {
Assert.IsNotNull(rk2);
}
}
[Test]
public void OpenSubKey_ReturnsNull_ForInvalidSubkey() {
string subkey =
"{F28A3464-0A5D-48FB-AFF6-B07F058D3EFC}";
RegistryKey rk = RegistryExtensions.OpenSubKey(
Registry.LocalMachine, subkey, false,
RegWow64Options.None);
Assert.IsNull(rk);
}
[Test]
public void PtrToRegistryKey_Returns_ValidValie() {
RegistryKey rk = Registry.LocalMachine;
IntPtr rkPtr = rk.Handle.DangerousGetHandle();
RegistryKey rk2 = RegistryExtensions
.PtrToRegistryKey(rkPtr, false, false,
RegWow64Options.None);
IntPtr rkPtr2 = rk2.Handle.DangerousGetHandle();
Assert.AreEqual(rkPtr, rkPtr2);
}
[Test]
public void PtrToRegistryKey_ReturnsNull_ForZero() {
RegistryKey rk = RegistryExtensions
.PtrToRegistryKey(IntPtr.Zero, false, false,
RegWow64Options.None);
Assert.IsNull(rk);
}
}
}
* RegistryExtensionsTests.cs
* © Andrey Bushman, 2016
*
* Integration tests of Bushman.AcProducts.RegistryExtensions
* class.
*/
using System;
using Microsoft.Win32;
using NUnit.Framework;
namespace Bushman.AcProducts.IntegrationTests {
[TestFixture]
public class RegistryExtensionsTests {
[Test]
public void GetPtr_Returns_ValidPtr() {
// `Registry.LocalMachine` key always exists.
RegistryKey rk = Registry.LocalMachine;
IntPtr rkPtr = rk.Handle.DangerousGetHandle();
/* The `RegistryKey.Handle` property appears since
* .Net 4.0, therefore for .Net 3.5 in the code
* under test I get it through reflection. */
IntPtr rkPtr2 = RegistryExtensions.GetPtr(rk);
Assert.AreEqual(rkPtr, rkPtr2);
}
[Test]
public void GetPtr_ReturnsZero_ForNull() {
/* The `RegistryKey.Handle` property appears since
* .Net 4.0, therefore for .Net 3.5 in the code
* under test I get it through reflection. */
IntPtr ptr = RegistryExtensions.GetPtr(null);
Assert.AreEqual(IntPtr.Zero, ptr);
}
[TestCase(@"SOFTWARE\7-Zip", RegWow64Options
.KEY_WOW64_64KEY, false)]
[TestCase(@"SOFTWARE\7-Zip", RegWow64Options
.KEY_WOW64_32KEY, true)]
[TestCase(@"SOFTWARE\Notepad++",
RegWow64Options.KEY_WOW64_64KEY, true)]
[TestCase(@"SOFTWARE\Notepad++",
RegWow64Options.KEY_WOW64_32KEY, false)]
[Description("7-Zip and Notepad++ are to be installed"
)]
public void OpenSubKey_ReturnsValidValue(string subkey,
RegWow64Options opt, bool isNull) {
RegistryKey rk = Registry.LocalMachine;
RegistryKey rk2 = RegistryExtensions.OpenSubKey(rk,
subkey, false, opt);
if (isNull) {
Assert.IsNull(rk2);
}
else {
Assert.IsNotNull(rk2);
}
}
[Test]
public void OpenSubKey_ReturnsNull_ForInvalidSubkey() {
string subkey =
"{F28A3464-0A5D-48FB-AFF6-B07F058D3EFC}";
RegistryKey rk = RegistryExtensions.OpenSubKey(
Registry.LocalMachine, subkey, false,
RegWow64Options.None);
Assert.IsNull(rk);
}
[Test]
public void PtrToRegistryKey_Returns_ValidValie() {
RegistryKey rk = Registry.LocalMachine;
IntPtr rkPtr = rk.Handle.DangerousGetHandle();
RegistryKey rk2 = RegistryExtensions
.PtrToRegistryKey(rkPtr, false, false,
RegWow64Options.None);
IntPtr rkPtr2 = rk2.Handle.DangerousGetHandle();
Assert.AreEqual(rkPtr, rkPtr2);
}
[Test]
public void PtrToRegistryKey_ReturnsNull_ForZero() {
RegistryKey rk = RegistryExtensions
.PtrToRegistryKey(IntPtr.Zero, false, false,
RegWow64Options.None);
Assert.IsNull(rk);
}
}
}
==================================================================
Комментариев нет:
Отправить комментарий