How to save a persistent value for application access across multiple runs?

Viewed 66

As the title states, I would like to store a variable (which will always be an positive integer < 10,000) for my application to access and process as needed across multiple runs.

My current implementation simply saves the value to a file, in the current directory, and then reads it in when needed.

#include<fstream>

int x = 5;
std::ofstream write_file(file_handle);
write_file << value;
write_file.close();

However, I'm not too keen on the idea of having an orphaned text file if the user decides to place the .exe on their desktop.

So, what other options do I have to store the value?

I'm primarily concerned with Windows 8+.

1 Answers

There is nothing wrong with using a file, but you don't have to (and should not) store it in the same folder as the .exe file, as it may not work depending on where the .exe is located (for instance, non-admins can't write to Program Files).

Windows sets aside special folders in the user's profile just for application-generated data, so you should store the file in one of those folders instead. Use the Win32 SHGetFolderPath() or SHGetKnownFolderPath() function to discover where those special folders are located, and then you should create a sub-folder for your application's use (you can even use SHGetFolderPathAndSubDir() for that purpose).

For example:

#include <fstream>
#include <filesystem>

#include <windows.h>
#include <shlobj.h>

namespace fs = std::filesystem;

fs::path pathToMyValueFile()
{
    WCHAR szPath[MAX_PATH];
    if (SHGetFolderPathAndSubDirW(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, L"MyApp", szPath) != S_OK) {
        throw ...;
    }
    return fs::path(szPath) / L"value.dat";

    /* alternatively:

    if (SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, szPath) != S_OK)
        throw ...;

    fs::path folder = fs::path(szPath) / L"MyApp";
    fs::create_directory(folder);

    return folder / L"value.dat";
    */
}

...

int value = 0;
std::ifstream read_file(pathToMyValueFile());
if (read_file.is_open()) {
    read_file >> value;
    read_file.close();
}

...

int value = 5;
std::ofstream write_file(pathToMyValueFile());
if (write_file.is_open()) {
    write_file << value;
    write_file.close();
}

In the future, if the user ever uninstalls your app, be sure to delete that subfolder.


Otherwise, you can store the value in the Windows Registry instead. Create a new key for your application under HKEY_CURRENT_USER\Software or HKEY_LOCAL_MACHINE\Software as needed, and then you can create values inside that key.

For example:

#include <fstream>
#include <windows.h>

int readMyValue()
{
    int value;

    HKEY hKey;
    LSTATUS lRes = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\MyApp", 0, KEY_QUERY_VALUE, &hKey);
    if (lRes == ERROR_SUCCESS)
    {
        DWORD size = sizeof(value);
        lRes = RegQueryValueExA(hKey, "Value", NULL, NULL, (LPBYTE)&value, size);
        RegCloseKey(hKey);
    }

    if (lRes != ERROR_SUCCESS)
    {
        if (lRes != ERROR_FILE_NOT_FOUND)
            throw ...;

        value = 0;
    }

    return value;
}

void saveMyValue(int value)
{
    HKEY hKey;
    LSTATUS lRes = RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\MyApp", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL);
    if (lRes == ERROR_SUCCESS)
    {
        DWORD size = sizeof(value);
        lRes = RegSetValueExA(hKey, "Value", 0, REG_DWORD, (BYTE*)&value, sizeof(value));
        RegCloseKey(hKey);
    }

    if (lRes != ERROR_SUCCESS)
        throw ...;

    /* alternatively:

    if (RegSetKeyValueA(HKEY_CURRENT_USER, "Software\\MyApp", "Value", REG_DWORD, &value, sizeof(value)) != ERROR_SUCCESS)
        throw ...;
    */
}

...

int value = readMyValue();

...

int value = 5;
saveMyValue(value);

If your app is uninstalled later, be sure to delete the Registry key.

Related