Есть ли правильный способ проверить, действителен ли HKEY/PHKEY?

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 ничего не дал мне, если она там есть, я могу быть слепым или она не использует имя функции, которое не очевидно для такого любителя, как я.

Если открыть/создать/и т.д. удалось, вы должны закрыть ключ, когда закончите с ним.

Retired Ninja 11.02.2023 14:30

@RetiredNinja структура объекта такова: когда я закончу с ним, этот ключ также не будет использоваться, поэтому он находится в деструкторе. Причина, по которой я спрашиваю, заключается в том, что я не уверен в поведении при вызове RegCloseKey на недействительном HKEY.

Azriel Elijay 11.02.2023 14:37

Является ли это исключительным случаем, если открытие раздела реестра не удается? Если это так, то конструктор или открывающая функция-член должны бросить вызов, а RAII позаботится об этой проблеме.

Richard Critten 11.02.2023 14:41

Способ, которым вы знаете, что дескриптор действителен, заключается в том, что вы получили его от одной из функций (создать, открыть и т. д.), которые его возвращают. Когда вы закончите с этим, вы позвоните RegCloseKey. Это так просто. Как правило, время между получением дескриптора, его использованием для каких-либо действий и его закрытием очень короткое, как промежуток одной функции. Если вы держите их так долго, что не знаете, действительны ли они по-прежнему, вы, вероятно, делаете это неправильно. Даже примечания к RegCloseKey говорят: «Ручки ключей не следует оставлять открытыми дольше, чем это необходимо».

Retired Ninja 11.02.2023 14:54

@RetiredNinja Ответственность объекта заключается в следующем: он проверяет, есть ли ключ, если он есть, то он извлекает/изменяет значение, если нет, то создает ключ, затем устанавливает значение, затем объект уничтожается. Так что я не держу их дольше, как мне нужно. Я завернул его в объект, так как хочу обеспечить закрытие HKEY во время его уничтожения, и он также используется во многих частях приложения. Как я уже упоминал, если это действительно единственный способ «Проверить», так как будет время, когда будет открыт только 1 из HKEY, поэтому Объект должен знать. Отсюда текущий код.

Azriel Elijay 11.02.2023 15:08

@RetiredNinja Возможно, правильным термином для текущего Конструктора/Деструктора было бы «Я хотел передать ответственность за знание самому Объекту».

Azriel Elijay 11.02.2023 15:14

Это не похоже на полезную абстракцию. Если вам нужен объект, который убирает за собой, заставьте его c'tor получать ресурсы и бросайте, если он терпит неудачу. Таким образом, вам не нужно будет иметь дело с выделением специального значения; объект всегда будет иметь допустимое значение или вообще не будет существовать. Если получение ресурса состоит из нескольких шагов, используйте функцию для инкапсуляции этой логики. Пусть он возвращает значение того типа, который управляет вашими ресурсами. Короче говоря: сделайте невозможным создание объекта, который хранит недопустимые данные.

IInspectable 11.02.2023 17:48

@IInspectable Я, хотя это именно то, что я делал, поскольку HKEY будет действительным HKEY только тогда, когда функция, которая его использует, фактически возвращает ERROR_SUCCESS, следовательно, они либо ничего, либо действительны. Это только конструкторы и деструкторы, а не весь объект, так как я только хотел знать, есть ли «правильный» способ проверки правильности HKEY при использовании winreg.h или есть что-то включенное в API, что я, возможно, пропустил , как я уже упоминал в исходном вопросе.

Azriel Elijay 11.02.2023 18:09

@IInspectable, и скажем, я переместил приобретение HKEY в конструктор, что было бы легко и сделало бы невозможным создание объекта, если у него не будет действительного HKEY. Это все еще не отвечает на мой вопрос. Но Ваш комментарий имеет смысл. Предотвращение создания недопустимых дескрипторов приведет к тому, что объект не будет заботиться о том, действителен он или нет, поскольку он будет «всегда» действителен. Но мне все равно хотелось бы узнать, является ли единственный способ узнать, действителен ли HKEY, просто проверив указатель?

Azriel Elijay 11.02.2023 18:15

они либо Ничто, либо Действительны" - вот в чем проблема. Почему бы вам не сделать невозможным создание экземпляра класса, если значения не действительны? Как только вы установите этот инвариант, больше не потребуется никаких проверок.

IInspectable 11.02.2023 18:16

@IInspectable Извините, если я безжалостен, но почему-то я просто действительно хочу знать, действительно ли у API не было такого, чтобы проверить, действительно ли он жив / действителен. Поскольку был экземпляр, который RegCreateKeyExA вернул ERROR_SUCCESS, но когда был использован HKEY, это привело к нарушению прав доступа, именно тогда я начал перемещать его в класс и задал этот вопрос. Я знал, что это был HKEY, так как при проверке среды он сказал: «Невозможно достичь недопустимой ячейки памяти». Но ваш комментарий попал в мой список дел, когда я буду рефакторить его сегодня вечером.

Azriel Elijay 11.02.2023 18:31

@AzrielElijay «Я просто действительно хочу знать, действительно ли у API не было такого, чтобы проверить, действительно ли он жив / действителен». - нет API, чтобы проверить это. API, который дает вам дескриптор, либо работает успешно, либо терпит неудачу, вы несете ответственность за проверку этого условия и игнорирование дескриптора в случае сбоя. Предоставление API недопустимого дескриптора является неопределенным поведением, если не указано иное.

Remy Lebeau 11.02.2023 20:13

@AzrielElijay «был случай, когда RegCreateKeyExA вернул ERROR_SUCCESS, но когда HKEY был использован, это привело к нарушению прав доступа» — единственный способ, который может произойти, — это если вы перезаписали сам HKEY таким образом, что это сделало его недействительным, или другой параметры, которые вы использовали вместе с HKEY, были каким-то образом недействительны.

Remy Lebeau 11.02.2023 20:15

@RemyLebeau Возможно, у меня была версия кода, которая привела к этой ошибке, поскольку я преобразовал ее в объект, как только получил нарушение прав доступа. Спасибо, что подтвердили, что winreg.h, не включил его, так как HKEY никогда не будет затронут, если он не вернется успешно. Итак, я думаю, что мой текущий подход достаточно хорош, но было бы лучше, если бы объект не мог быть создан, если HKEY и все другие параметры не являются допустимыми, как предложил IInspectable.

Azriel Elijay 11.02.2023 20:30

Спасибо за все ваши комментарии Ричард Криттен, IInspectable, Реми Лебо!

Azriel Elijay 11.02.2023 20:31

Не ответ, но если вы используете Visual Studio, есть класс CRegKey (с ATL): Learn.microsoft.com/en-us/cpp/atl/reference/cregkey-class это довольно хороший помощник реестра RAII и исходники открыты

Simon Mourier 12.02.2023 21:08

@SimonMourier говорит о том, чтобы выстрелить себе в ногу, но это жизнь. В некоторых вы выигрываете, а в некоторых теряете много времени.

Azriel Elijay 12.02.2023 21:11
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
17
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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.

Azriel Elijay 12.02.2023 21:08

@AzrielElijay Ну, мой плохой, я полностью проигнорировал тег visual-studio-2008, но я думаю, что «вам придется записать эту информацию отдельно» считается ответом. Теперь, если вас интересует руководство по проектированию API (которое будет работать с любым C++), я дам несколько заметок о том, как лучше структурировать ваш код, не требуя C++17. Это будет самоуверенно, сильно при этом.

IInspectable 12.02.2023 21:23

Ваши более ранние комментарии к этому вопросу приобретали все больше и больше смысла, чем больше я думал об этом, и я последовал вашему совету и сделал невозможным создание экземпляра класса без гарантии того, что он будет иметь недопустимые дескрипторы или значения. На данный момент я разделил их на два класса, чтобы убедиться, что, по крайней мере, любая функция, использующая класс, не сможет создать его экземпляр, если он будет передан с параметрами, которые не обеспечат правильное HKEY. Будучи самоучкой и все такое, я рад, что многие из вас хотя бы понимают мою проблему.

Azriel Elijay 12.02.2023 21:35

Другие вопросы по теме

Как установить SetWindowsHookEx только в определенном окне для прослушивания событий нажатия левой кнопки мыши
Можно ли отправить поток из данных BLOB-объектов в базе данных в PlaySound (MMSystem) вместо указания имени файла?
Нарисуйте BITMAP поверх дескриптора окна (HWND) другого процесса
Почему SetFilePointerEx(1) успешно работает с пустым файлом, который открыт только для чтения?
Имеет ли значение для CreateFile, были ли открыты другие дескрипторы того же файла тем же или другим процессом?
Как использовать дескриптор в WINAPI, когда я реализую свой код С++ с принципом RAII?
Как использовать спецификатор формата Z в Microsoft printf (и wprintf)?
Сохранение данных из HANDLE в файл и загрузка данных из файла в HANDLE без потери данных
Приложение аварийно завершает работу при закрытии диалогового окна во время выполнения асинхронных операций
Visual Studio жалуется на несколько аргументов VirtualAlloc, несмотря на правильные аргументы