1
\$\begingroup\$

Regarding an application written in C++ using the Windows API. I'd like to store a user setting like the window position on program exit, to the Windows registry and retrieve settings like these on the next start of the program.

I currently have 3 settings to be stored/retrieved to/from the registry.

typedef struct tagWINPOS
{
  DWORD dwWindowStyle;
  int iWindowX;
  int iWindowY;
} winpos_t;

HKEY hKeyApp = (HKEY)INVALID_HANDLE_VALUE;
const char szWindowStyle[] = "WindowStyle";
const char szWindowX[] = "WindowX";
const char szWindowY[] = "WindowY";

Later on in the code I'm calling functions like RegSetValueEx and RegQueryValueEx always three times. Like so:

int SaveSettings( HWND hWnd )
{
  winpos_t wpos;
  // window dimensions from hWnd are stored to wpos, not shown 

  RegSetValueEx( hKeyApp, szWindowStyle, 0,
    REG_DWORD,
    (const BYTE*)&wpos.dwWindowStyle,
    sizeof(wpos.dwWindowStyle) );
  RegSetValueEx( hKeyApp, szWindowX, 0,
    REG_DWORD,
    (const BYTE*)&wpos.iWindowX,
    sizeof(wpos.iWindowX) );
  RegSetValueEx( hKeyApp, szWindowY, 0,
    REG_DWORD,
    (const BYTE*)&wpos.iWindowY,
    sizeof(wpos.iWindowY) );

  // further irrelevant code removed
}

Things start to look a bit tedious. So I was thinking to put all the parameters for one function call (say RegSetValueEx) into an std::array and then make a for loop iterating over all the array elements.

Because the Windows registry can store multiple data types, like DWORD (32-bit), QWORD (64-bit) and strings. I thought about std::variant to list all of these for this data field.

I'm started with a little test program like this:

#include <cstddef>
#include <cstdio>
#include <cstdint>
#include <tuple>
#include <variant>

typedef std::variant< int32_t, int64_t, char* > data_t;

typedef std::tuple<
  data_t,       // setting data
  const char*,  // setting name
  std::size_t   // size of setting [bytes]
> setting_t;

typedef std::array< setting_t, 2 > settings_t;

constexpr int reg_dword = 0;
constexpr int reg_qword = 1;

constexpr settings_t settings{
  std::make_tuple( (std::in_place_index<reg_dword>, 33), "Mydata", 4 ),
  std::make_tuple( (std::in_place_index<reg_qword>, 34), "Mydata2", sizeof(std::in_place_index_t<reg_qword>) ) };

int main()
{
  printf( "name: %s, value %ld\n", std::get<1>( settings[0] ), std::get<0>( std::get<0>( settings[0] ) ));

  getchar();
  return EXIT_SUCCESS;
}

Anyway, do people consider this approach a good one to get to my goal of having to spell out a single function call of RegSetValueEx inside a for loop?

The initialization of the array has much boilerplate as well as the effort to obtain values. What is a good approach to improve the readability of this? Maybe a constexpr function like std::make_tuple but then more specific for my case?

\$\endgroup\$
2
  • \$\begingroup\$ Why are you using a tuple instead of a class with named members? \$\endgroup\$ Commented Feb 5, 2019 at 4:31
  • \$\begingroup\$ A class or struct instead of the tuple might be an idea. Can you tell me how I should initialize the std::variant through the class in that case? \$\endgroup\$
    – Zeyneb
    Commented Feb 5, 2019 at 4:46

1 Answer 1

1
\$\begingroup\$

you need save winpos_t as single value with REG_BINARY instead 3 different values. possible implementation:

class CConfig
{
    HKEY _hKey;
public:

    ~CConfig()
    {
        if (_hKey)
        {
            RegCloseKey(_hKey);
        }
    }

    CConfig() : _hKey (0)
    {
    }

    LSTATUS Save(
        PCWSTR lpValueName,
        DWORD dwType,
        const void* lpData,
        DWORD cbData)
    {
        return RegSetValueExW(_hKey, lpValueName, 0, dwType, (BYTE*)lpData, cbData);
    }

    LSTATUS Load(
        PCWSTR lpValueName,
        PDWORD lpType,
        void* lpData,
        PDWORD lpcbData)
    {
        return RegQueryValueExW(_hKey, lpValueName, 0, lpType, (BYTE*)lpData, lpcbData);
    }

    LSTATUS Init(HKEY hKey, PCWSTR lpSubKey)
    {
        return RegCreateKeyExW(hKey, lpSubKey, 0, 0, 0, KEY_ALL_ACCESS, 0, &_hKey, 0);
    }
};

void test_cfg()
{
    CConfig cfg;
    if (!cfg.Init(HKEY_CURRENT_USER, L"Software\\MyKey"))
    {
        struct winpos_t
        {
            DWORD dwWindowStyle;
            int iWindowX;
            int iWindowY;
        };

        winpos_t test_pos = { 1, 2, 3};

        static const PCWSTR szwinpos_t = L"winpos_t";

        if (!cfg.Save(szwinpos_t, REG_BINARY, &test_pos, sizeof(test_pos)))
        {
            ULONG type, cb = sizeof(test_pos);
            RtlZeroMemory(&test_pos, sizeof(test_pos));
            if (!cfg.Load(szwinpos_t, &type, &test_pos, &cb) && type == REG_BINARY && cb == sizeof(test_pos))
            {
                DbgPrint("{%x, %x, %x}\n", test_pos.dwWindowStyle, test_pos.iWindowX, test_pos.iWindowY);
            }
        }
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.