REGISTRY()
{
/*HKEY*/m_pRoot = nullptr;
/*HKEY*/m_pReturnKey = nullptr;
m_pPath = nullptr;
m_pValueName = nullptr;
}
~REGISTRY()
{
if ( m_pReturnKey )
{
RegCloseKey(m_pReturnKey);
m_pReturnKey = nullptr;
}
if ( m_pRoot )
{
RegCloseKey(m_pRoot);
m_pRoot = nullptr;
}
}
У меня есть класс, который обрабатывает все функции реестра моих приложений, я позволяю деструктору объекта обрабатывать закрытие HKEYs (как показано выше). Я принял ответ , представленный здесь , но вместо использования HANDLES я просто установил HKEYs как nullptr во время построения объекта и проверил, не nullptr ли это во время разрушения, и закрыл его. Я также использую Smart Pointers<unique_ptr> при его использовании, чтобы убедиться, что деструктор вызывается, даже когда он выдает исключение.
Поскольку после проверки, когда RegOpenKeyExA , RegCreateKeyExA , RegSetValueEx или RegQueryValueExA возвращает что-либо, кроме ERROR_SUCCESS, он ничего не делает с переданным HKEY и остается как nullptr.
Помимо этого метода, существует ли правильный способ проверить, активен и действителен ли HKEY/PHKEY? Просмотр страницы MSDN на winreg.h ничего не дал мне, если она там есть, я могу быть слепым или она не использует имя функции, которое не очевидно для такого любителя, как я.
@RetiredNinja структура объекта такова: когда я закончу с ним, этот ключ также не будет использоваться, поэтому он находится в деструкторе. Причина, по которой я спрашиваю, заключается в том, что я не уверен в поведении при вызове RegCloseKey на недействительном HKEY.
Является ли это исключительным случаем, если открытие раздела реестра не удается? Если это так, то конструктор или открывающая функция-член должны бросить вызов, а RAII позаботится об этой проблеме.
Способ, которым вы знаете, что дескриптор действителен, заключается в том, что вы получили его от одной из функций (создать, открыть и т. д.), которые его возвращают. Когда вы закончите с этим, вы позвоните RegCloseKey. Это так просто. Как правило, время между получением дескриптора, его использованием для каких-либо действий и его закрытием очень короткое, как промежуток одной функции. Если вы держите их так долго, что не знаете, действительны ли они по-прежнему, вы, вероятно, делаете это неправильно. Даже примечания к RegCloseKey говорят: «Ручки ключей не следует оставлять открытыми дольше, чем это необходимо».
@RetiredNinja Ответственность объекта заключается в следующем: он проверяет, есть ли ключ, если он есть, то он извлекает/изменяет значение, если нет, то создает ключ, затем устанавливает значение, затем объект уничтожается. Так что я не держу их дольше, как мне нужно. Я завернул его в объект, так как хочу обеспечить закрытие HKEY во время его уничтожения, и он также используется во многих частях приложения. Как я уже упоминал, если это действительно единственный способ «Проверить», так как будет время, когда будет открыт только 1 из HKEY, поэтому Объект должен знать. Отсюда текущий код.
@RetiredNinja Возможно, правильным термином для текущего Конструктора/Деструктора было бы «Я хотел передать ответственность за знание самому Объекту».
Это не похоже на полезную абстракцию. Если вам нужен объект, который убирает за собой, заставьте его c'tor получать ресурсы и бросайте, если он терпит неудачу. Таким образом, вам не нужно будет иметь дело с выделением специального значения; объект всегда будет иметь допустимое значение или вообще не будет существовать. Если получение ресурса состоит из нескольких шагов, используйте функцию для инкапсуляции этой логики. Пусть он возвращает значение того типа, который управляет вашими ресурсами. Короче говоря: сделайте невозможным создание объекта, который хранит недопустимые данные.
@IInspectable Я, хотя это именно то, что я делал, поскольку HKEY будет действительным HKEY только тогда, когда функция, которая его использует, фактически возвращает ERROR_SUCCESS, следовательно, они либо ничего, либо действительны. Это только конструкторы и деструкторы, а не весь объект, так как я только хотел знать, есть ли «правильный» способ проверки правильности HKEY при использовании winreg.h или есть что-то включенное в API, что я, возможно, пропустил , как я уже упоминал в исходном вопросе.
@IInspectable, и скажем, я переместил приобретение HKEY в конструктор, что было бы легко и сделало бы невозможным создание объекта, если у него не будет действительного HKEY. Это все еще не отвечает на мой вопрос. Но Ваш комментарий имеет смысл. Предотвращение создания недопустимых дескрипторов приведет к тому, что объект не будет заботиться о том, действителен он или нет, поскольку он будет «всегда» действителен. Но мне все равно хотелось бы узнать, является ли единственный способ узнать, действителен ли HKEY, просто проверив указатель?
они либо Ничто, либо Действительны" - вот в чем проблема. Почему бы вам не сделать невозможным создание экземпляра класса, если значения не действительны? Как только вы установите этот инвариант, больше не потребуется никаких проверок.
@IInspectable Извините, если я безжалостен, но почему-то я просто действительно хочу знать, действительно ли у API не было такого, чтобы проверить, действительно ли он жив / действителен. Поскольку был экземпляр, который RegCreateKeyExA вернул ERROR_SUCCESS, но когда был использован HKEY, это привело к нарушению прав доступа, именно тогда я начал перемещать его в класс и задал этот вопрос. Я знал, что это был HKEY, так как при проверке среды он сказал: «Невозможно достичь недопустимой ячейки памяти». Но ваш комментарий попал в мой список дел, когда я буду рефакторить его сегодня вечером.
@AzrielElijay «Я просто действительно хочу знать, действительно ли у API не было такого, чтобы проверить, действительно ли он жив / действителен». - нет API, чтобы проверить это. API, который дает вам дескриптор, либо работает успешно, либо терпит неудачу, вы несете ответственность за проверку этого условия и игнорирование дескриптора в случае сбоя. Предоставление API недопустимого дескриптора является неопределенным поведением, если не указано иное.
@AzrielElijay «был случай, когда RegCreateKeyExA вернул ERROR_SUCCESS, но когда HKEY был использован, это привело к нарушению прав доступа» — единственный способ, который может произойти, — это если вы перезаписали сам HKEY таким образом, что это сделало его недействительным, или другой параметры, которые вы использовали вместе с HKEY, были каким-то образом недействительны.
@RemyLebeau Возможно, у меня была версия кода, которая привела к этой ошибке, поскольку я преобразовал ее в объект, как только получил нарушение прав доступа. Спасибо, что подтвердили, что winreg.h, не включил его, так как HKEY никогда не будет затронут, если он не вернется успешно. Итак, я думаю, что мой текущий подход достаточно хорош, но было бы лучше, если бы объект не мог быть создан, если HKEY и все другие параметры не являются допустимыми, как предложил IInspectable.
Спасибо за все ваши комментарии Ричард Криттен, IInspectable, Реми Лебо!
Не ответ, но если вы используете Visual Studio, есть класс CRegKey (с ATL): Learn.microsoft.com/en-us/cpp/atl/reference/cregkey-class это довольно хороший помощник реестра RAII и исходники открыты
@SimonMourier говорит о том, чтобы выстрелить себе в ногу, но это жизнь. В некоторых вы выигрываете, а в некоторых теряете много времени.





API winreg не отбрасывает значение, представляющее недопустимый HKEY (в отличие, скажем, от fileapi , где CreateFileW использует для этой цели INVALID_HANDLE_VALUE). Вместо этого вызовы API сообщают, были ли они успешными, и только после этого гарантируют запись HKEY в место, указанное вызывающей стороной.
Если вашей реализации необходимо знать, представляет ли какая-либо часть информации достоверные данные, вам придется записывать эту информацию отдельно. Начиная с C++17, std::Optional является стандартным инструментом для этой цели.
Таким образом, возможным решением вашей проблемы может быть:
class REGISTRY
{
std::optional<HKEY> m_pRoot;
std::optional<HKEY> m_pReturnKey;
// ...
}
REGISTRY::~REGISTRY()
{
if (m_pReturnKey)
// equivalent to
// if (m_pReturnKey.has_value())
{
RegCloseKey(m_pReturnKey.value());
m_pReturnKey = {};
}
// ...
}
m_pRoot и m_pReturnKey инициализируются по умолчанию, то есть они содержат значение типа std::nullopt_t. Д'тор будет наблюдать за этим и ничего не делать. Что решает проблему, как указано.
Спасибо за отзыв! Но, поскольку в моих тегах указано, что проект, к которому я применяю это, компилируется с использованием MSVC2008SP1, поэтому я ограничен C++ 03 с boost 1.5, я бы хотел перевести проект на более высокий стандарт, если бы мог, но Третий Партийные библиотеки не пускают меня. так что std::optional не может быть и речи. Но это дает простой и понятный ответ на мой вопрос о том, что API winreg действительно не имеет ничего для проверки HKEY и гарантирует его достоверность только тогда, когда возвращается ERROR_SUCCESS.
@AzrielElijay Ну, мой плохой, я полностью проигнорировал тег visual-studio-2008, но я думаю, что «вам придется записать эту информацию отдельно» считается ответом. Теперь, если вас интересует руководство по проектированию API (которое будет работать с любым C++), я дам несколько заметок о том, как лучше структурировать ваш код, не требуя C++17. Это будет самоуверенно, сильно при этом.
Ваши более ранние комментарии к этому вопросу приобретали все больше и больше смысла, чем больше я думал об этом, и я последовал вашему совету и сделал невозможным создание экземпляра класса без гарантии того, что он будет иметь недопустимые дескрипторы или значения. На данный момент я разделил их на два класса, чтобы убедиться, что, по крайней мере, любая функция, использующая класс, не сможет создать его экземпляр, если он будет передан с параметрами, которые не обеспечат правильное HKEY. Будучи самоучкой и все такое, я рад, что многие из вас хотя бы понимают мою проблему.
Если открыть/создать/и т.д. удалось, вы должны закрыть ключ, когда закончите с ним.