Перечисление подразделов с помощью CSettingsStore

Я рассматривал использование класса CSettingsStore.

Я знаю, как прочитать значение из реестра. Пример:

CSettingsStore store(TRUE, TRUE);

if (store.Open(_T("Software\\TruckleSoft\\VisitsRota")))
{
    if (store.Read(_T("AppPath"), m_strPathVisitsRota))
    {
        //yes, but is the path still valid
        if (!PathFileExists(m_strPathVisitsRota))
        {
            // it exists
            m_strPathVisitsRota = _T("");
        }
    }
}

Теперь в документации указано:

The security access depends on the bReadOnly parameter. If bReadonly is FALSE, the security access will be set to KEY_ALL_ACCESS. If bReadyOnly is TRUE, the security access will be set to a combination of KEY_QUERY_VALUE, KEY_NOTIFY and KEY_ENUMERATE_SUB_KEYS.

Таким образом, это означает, что вы можете перечислить подключи. Но я не могу найти пример, объясняющий перечисление набора пар ключ/значение с использованием этого класса.

Я не знаком с этим классом, но, похоже, у него нет метода перечисления. У него есть ATL::CRegKey m_reg, но он защищен. Вы должны создать свой собственный класс class CMySettingsStore : public CSettingsStore, использовать m_reg для перечисления ключей.

Barmak Shemirani 11.04.2019 01:27

@BarmakShemirani Возможно, я запрошу это как функцию. Кажется разумным улучшением.

Andrew Truckle 11.04.2019 06:33

Отвечает ли Microsoft на запросы обновления MFC? Наверное, проще просто написать.

Barmak Shemirani 11.04.2019 08:45

@BarmakShemirani Я разместил это здесь: developercommunity.visualstudio.com/idea/528425/…

Andrew Truckle 11.04.2019 09:41

@BarmakShemirani Они не отвечают уже 2 года. Так что я думаю, что я должен написать это. Это единственное, что заставляет меня сейчас использовать другой набор классов.

Andrew Truckle 12.11.2021 22:32
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
88
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Этот ответ находится в стадии разработки.

Заголовок

#pragma once
#include <afxsettingsstore.h>
#include <vector>

class CMySettingsStore :
    public CSettingsStore
{
public:
    CMySettingsStore(BOOL bAdmin, BOOL bReadOnly);
    void EnumKey(std::vector<CString> &vecKeys);
};

Источник

#include "pch.h"
#include "CMySettingsStore.h"

CMySettingsStore::CMySettingsStore(BOOL bAdmin, BOOL bReadOnly)
    : CSettingsStore(bAdmin, bReadOnly)
{

}

void CMySettingsStore::EnumKey(std::vector<CString>& vecKeys)
{
    DWORD dwIndex = 0;
    long lResult = ERROR_SUCCESS;

    vecKeys.clear();

    while (lResult == ERROR_SUCCESS)
    {
        CString strSubKey;
        DWORD dwLength = _MAX_PATH;
        lResult = m_reg.EnumKey(dwIndex, strSubKey.GetBuffer(_MAX_PATH), &dwLength);
        strSubKey.ReleaseBuffer();

        dwIndex++;

        vecKeys.push_back(strSubKey);
    }
}

Тестовый код

Код предполагает, что в вашем диалоговом окне есть элемент управления CListBox:

CMySettingsStore mySettings(FALSE, TRUE);

if (mySettings.Open(_T("Software\\xxxx")))
{
    std::vector<CString> list;
    mySettings.EnumKey(list);
    mySettings.Close();

    for (const auto& strSubKey : list)
    {
        m_lbData.AddString(strSubKey);
    }
}

Вышеприведенное работает и дает мне список подразделов для указанного ключа. Однако я еще не разобрался, как перечислить значения в заданном ключе.

lResult = m_reg.EnumKey(...) может потерпеть неудачу, мы должны немедленно сломаться, иначе мы получим лишний подключа со значением ""
Barmak Shemirani 13.11.2021 00:29

@BarmakShemirani Хороший вопрос. Я принял ваше предложение ответа и предоставил измененный код для вашего ответа.

Andrew Truckle 13.11.2021 10:09
Ответ принят как подходящий

Обратите внимание, что KEY_ENUMERATE_SUB_KEYS — это флаг, который запрашивает доступ для перечисления подразделов, на самом деле он этого не делает.

KEY_ALL_ACCESS — это комбинация разных флагов, в том числе KEY_ENUMERATE_SUB_KEYS. Таким образом, в обоих случаях запрашивается доступ к перечислению.

Для перечисления значений нам нужен ::RegEnumValue API, этот метод не обёрнут в CRegKey, но это не беда, мы можем получить HKEY напрямую.

Вот пример, основанный на вашем коде и Образец Майкрософт

void CMySettingsStore::EnumKeys(std::vector<CString>& vec)
{
    vec.clear();
    DWORD subkey_total;
    if (ERROR_SUCCESS != RegQueryInfoKey(m_reg.m_hKey, NULL, NULL, NULL, 
        &subkey_total, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
        return;
    wchar_t buf[1024];
    for (DWORD i = 0; i < subkey_total; i++)
    {
        DWORD len = _countof(buf);
        if (ERROR_SUCCESS == m_reg.EnumKey(i, buf, &len))
            vec.push_back(buf);
    }
}

void CMySettingsStore::EnumValues(std::vector<CString>& vec)
{
    vec.clear();
    DWORD values_total;
    if (ERROR_SUCCESS != RegQueryInfoKey(m_reg.m_hKey, NULL, NULL, NULL, 
        NULL, NULL, NULL, &values_total, NULL, NULL, NULL, NULL))
        return;
    wchar_t buf[1024];
    for (DWORD i = 0; i < values_total; i++)
    {
        DWORD len = _countof(buf);
        if (ERROR_SUCCESS == RegEnumValue(m_reg.m_hKey, i, buf, &len,
            NULL, NULL, NULL, NULL)) 
            vec.push_back(buf);
    }
}

Обновление от автора вопроса

Этот код не устраняет все предупреждения анализа кода, но добавляет обработку ошибок и поддержку получения значения типы:

#include "stdafx.h"
#include "MySettingsStore.h"

CMySettingsStore::CMySettingsStore(BOOL bAdmin, BOOL bReadOnly)
    : CSettingsStore(bAdmin, bReadOnly)
{

}

bool CMySettingsStore::EnumKeys(std::vector<CString>& vec)
{
    vec.clear();

    DWORD subkey_total{}, lResult{};
    if (RegQueryInfoKey(m_reg.m_hKey, NULL, NULL, NULL,
        &subkey_total, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
    {
        const DWORD dwError = ::GetLastError();
        AfxMessageBox(GetLastErrorAsString(dwError), MB_OK | MB_ICONWARNING);
        return false;
    }

    wchar_t buf[1024];
    for (DWORD i = 0; i < subkey_total; i++)
    {
        DWORD len = _countof(buf);
        lResult = m_reg.EnumKey(i, buf, &len);
        if (lResult == ERROR_SUCCESS || ERROR_NO_MORE_ITEMS)
            vec.push_back(buf);
    }

    if (lResult != ERROR_NO_MORE_ITEMS)
    {
        const DWORD dwError = ::GetLastError();
        AfxMessageBox(GetLastErrorAsString(dwError), MB_OK | MB_ICONWARNING);
        return false;
    }
    
    return true;
}

bool CMySettingsStore::EnumValues(std::map<CString, DWORD>& map)
{
    map.clear();
    DWORD values_total{}, dwType{}, lResult{};
    if (RegQueryInfoKey(m_reg.m_hKey, NULL, NULL, NULL,
        NULL, NULL, NULL, &values_total, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
    {
        const DWORD dwError = ::GetLastError();
        AfxMessageBox(GetLastErrorAsString(dwError), MB_OK | MB_ICONWARNING);
        return false;
    }

    wchar_t buf[1024];
    for (DWORD i = 0; i < values_total; i++)
    {
        DWORD len = _countof(buf);

        lResult = RegEnumValue(m_reg.m_hKey, i, buf, &len,
            NULL, &dwType, NULL, NULL);
        if (lResult == ERROR_SUCCESS || lResult == ERROR_NO_MORE_ITEMS)
            map.emplace(buf, dwType);
    }

    if (lResult != ERROR_NO_MORE_ITEMS)
    {
        const DWORD dwError = ::GetLastError();
        AfxMessageBox(GetLastErrorAsString(dwError), MB_OK | MB_ICONWARNING);
        return false;
    }

    return true;
}

CString CMySettingsStore::GetLastErrorAsString(DWORD dwError)
{
    LPVOID  lpMsgBuf{};
    CString strError;

    ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        nullptr,
        dwError,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR)&lpMsgBuf,
        0,
        nullptr);

    strError = static_cast<LPTSTR>(lpMsgBuf);

    LocalFree(lpMsgBuf);

    return strError;
}

Спасибо за ваш код. Я получаю несколько предупреждений анализа (распад указателя и т. д., а также использование nullptr вместо NULL. Но я могу посмотреть на это. Я посмотрю на добавление обработки ошибок, поскольку, как вы говорите, EnumKey возвращает определенное значение для успеха, завершения или ошибки. И для EnumValues мне пришлось передать std::map<CString, DWORD>, так как мне нужно было знать тип значения.

Andrew Truckle 13.11.2021 09:36

Я добавил вторую версию кода к вашему ответу - это нормально? Спасибо.

Andrew Truckle 13.11.2021 10:05

Единственное предупреждение, которое у нас есть (после замены NULL на nullptr, — это использование этого буфера и уменьшение указателя.

Andrew Truckle 13.11.2021 10:14

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