Win32 CryptProtectData how to save output data? - c++

https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata
How can I save the output key to the windows registry or a file? I've been trying to do this for two days now but no progress. One of the problems is that I am using Unreal Engine and they have their own string type "FString" which does not easily convert to std::string or const char*
This is the code I'm using to save to the registry, but I'm not sure if it's valid:
void SUGH1LoginWidget::SecureSaveJWTToken(FString JWT_Token)
{
#ifdef _WIN32
DATA_BLOB DataIn;
DATA_BLOB DataOut; // Key to decrypt the data. Store in windows registry.
DATA_BLOB EncryptionKey; // Uses define in ShooterGame.h to encrypt the data so that only the UGH1 application can access the token.
DWORD cbDataInput = lstrlenA((char*)TCHAR_TO_ANSI(*JWT_Token)) + 1;
DataIn.pbData = (BYTE*)ConvertFStringToCString(JWT_Token).get();
DataIn.cbData = cbDataInput;
BYTE* pbDataEncryptionKey = (BYTE*)TOKEN_ENCRYPTION_KEY;
DWORD cbDataEncryptionKey = strlen((char*)pbDataEncryptionKey) + 1;
EncryptionKey.pbData = pbDataEncryptionKey;
EncryptionKey.cbData = cbDataEncryptionKey;
// Finally save the data
CryptProtectData(&DataIn, NULL, &EncryptionKey, NULL, NULL, NULL, &DataOut);
SaveJWTDecryptionKeyToRegistry(DataOut);
#endif
}
void SUGH1LoginWidget::SaveJWTDecryptionKeyToRegistry(DATA_BLOB DecryptionKey)
{
FString Base64EncryptionKey = FBase64::Encode(DecryptionKey.pbData, DecryptionKey.cbData);
HKEY CurrentUserKey;
LSTATUS Error;
Error = RegOpenCurrentUser(KEY_ALL_ACCESS, &CurrentUserKey);
if (Error == ERROR_SUCCESS)
{
HKEY SoftwareKey;
Error = RegOpenKeyExA(CurrentUserKey, "SOFTWARE", NULL, KEY_WRITE, &SoftwareKey);
if (Error == ERROR_SUCCESS)
{
HKEY UGHStudiosSubKey;
Error = RegCreateKeyExA(SoftwareKey, "UGH Studios LLC", NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &UGHStudiosSubKey, NULL);
if (Error == ERROR_SUCCESS)
{
Error = RegSetValueExA(UGHStudiosSubKey, "LoginTokenDecryptionKey", NULL, REG_SZ, (BYTE*)TCHAR_TO_ANSI(*Base64EncryptionKey), Base64EncryptionKey.Len());
RegCloseKey(UGHStudiosSubKey);
RegCloseKey(SoftwareKey);
if (Error != ERROR_SUCCESS)
{
UE_LOG(LogLoginWidget, Error, TEXT("The game was unable to save the login token to the registry."));
}
}
}
}
else
{
UE_LOG(LogLoginWidget, Error, TEXT("The game was unable to access the windows registry on this machine. "));
}
RegCloseKey(CurrentUserKey);
}
This is my code for loading from the registry back into an FString but it's not working properly:
std::shared_ptr<char[]> SUGH1LoginWidget::ConvertFStringToCString(const FString InputString)
{
std::shared_ptr<char[]> ptr(new char[InputString.Len()]);
TArray<TCHAR> CharArray = InputString.GetCharArray();
for (int i = 0; i < InputString.Len(); i++)
{
ptr[i] = CharArray[i];
}
return ptr;
}
PRAGMA_DISABLE_OPTIMIZATION
FReply SUGH1LoginWidget::GetJWTTokenFromRegistry()
{
TArray<uint8> DecryptionKeyBytes;
// load base64 string from registry
LSTATUS Error;
HKEY UGHStudiosSubKey;
Error = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\UGH Studios LLC", NULL, KEY_READ, &UGHStudiosSubKey);
if (Error == ERROR_SUCCESS)
{
char buffer[8193] = { 0 };
DWORD dwLen = 8192;
DWORD dwType = 0;
Error = RegQueryValueExA(UGHStudiosSubKey, "LoginTokenDecryptionKey", 0, &dwType, (BYTE*)buffer, &dwLen);
if (Error == ERROR_SUCCESS)
{
std::string outValue = buffer;
FString decoded_string;
//FString reg_value_as_fstring = FString(outValue.length(), outValue.c_str());
uint8* decoded = malloc();
FBase64::Decode(outValue.c_str(), outValue.size(), decoded);
//:Decode(const FString & Source, TArray<uint8>&OutDest
//::Decode(const CharType* Source, uint32 Length, uint8* Dest)
//UE_LOG(LogLoginWidget, Error, TEXT("Out value: %s"), *decoded_string);
}
//Error = RegQueryValueExA(UGHStudiosSubKey, "LoginTokenDecryptionKey", NULL, NULL, buffer, size);
return FReply::Handled();
//return decoded_string;
//std::string outvalue = (char*)buffer;
}
//FBase64::Decode(, DecryptionKeyBytes)
//return FString("");
return FReply::Handled();
}
PRAGMA_ENABLE_OPTIMIZATION
What's the standard way of saving the struct data? I was tying to Base64 encrypt it and save it as a registry key. But this doesn't work.

Related

C++ Win32 Getting a registry key

const char* Launcher::GetProjectName()
{
PVOID data;
LPDWORD pcbData;
HKEY OpenResult;
LSTATUS status = RegOpenKeyEx(HKEY_CURRENT_USER, L"Environment", NULL, KEY_READ, &OpenResult);
if (status != ERROR_SUCCESS)
{
LOG(ERROR) << "Could not found registry key 'Environment'";
}
else
{
LPCWSTR project_name_key = L"project_name";
DWORD data_type;
WCHAR value[255];
PVOID pvData = value;
DWORD size = sizeof(value);
status = RegGetValue(OpenResult, NULL, project_name_key, RRF_RT_ANY, &data_type, pvData, &size);
if (status != ERROR_SUCCESS)
{
LOG(ERROR) << "Could not found registry value 'project_name'";
}
else
{
switch (data_type)
{
case REG_DWORD:
wprintf(L"Value data: %x\n", *(DWORD*)pvData);
break;
case REG_SZ:
wprintf(L"Value data: %s\n", (PWSTR)pvData);
}
}
RegCloseKey(OpenResult);
}
return 0;
}
I'm trying to get this registry key I made named "project_name" and return it as a char* or a std::string. However, I'm getting garbage data in pvData. What am I doing wrong here? I've seen some other stackoverflow posts and tried to replicate their setup as well but nothing is working. My entire goal here is to retrieve an environment variable using the windows registry.
I think there's a mismatch of ANSI and Unicode expectations. Your code is likely compiling for ANSI, but you're passing a wide-char buffer. Let's just explicitly call the A version of the Registry functions so you can stay in the ANSI string space.
Instead of this:
WCHAR value[255];
PVOID pvData = value;
DWORD size = sizeof(value);
status = RegGetValue(OpenResult, NULL, project_name_key, RRF_RT_ANY, &data_type, pvData, &size);
Use this:
char value[255];
DWORD size = sizeof(value);
status = RegGetValueA(OpenResult, NULL, project_name_key, RRF_RT_ANY, &data_type, value, &size);
Then, return a std::string as follows:
Declare your function to return a string, not a pointer:
const std::string Launcher::GetProjectName()
Then simply return value as a string;
return std::string(value);

I am getting error 2 in my winreg function

My code:
HKEY hKey;
char *path = "SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001\\HwProfileGuid";
LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_ALL_ACCESS, &hKey);
QString q = QString::number(result);
if (result == ERROR_SUCCESS) {
QMessageBox messageBox1;
messageBox1.critical(0,"Error", "Success");
messageBox1.setFixedSize(500,200);
} else {
QMessageBox messageBox2;
messageBox2.critical(0,"Error", q);
messageBox2.setFixedSize(500,200);
}
The error I am getting:
Where the key is in my Registry:
I think the problem is related to the way I put the info in the path variable, but I am not sure.
You have HwProfileGuid in the wrong place.
HwProfileGuid is a value inside of the 0001 key, but you are trying to open HwProfileGuid as a sub-key of 0001 instead, which is why you are getting error 2 (ERROR_FILE_NOT_FOUND), because there is no sub-key named HwProfileGuid.
Also, KEY_ALL_ACCESS is too many rights to request just to read a value from a key. Use KEY_QUERY_VALUE instead. Don't request more rights than you actually need.
Try this:
const char *path = "SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001";
const char *valueName = "HwProfileGuid";
char guid[40] = {0};
HKEY hKey;
LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS) {
DWORD size = sizeof(guid);
result = RegQueryValueExA(hKey, valueName, NULL, NULL, reinterpret_cast<LPBYTE>(guid), &size);
RegCloseKey(hKey);
}
QMessageBox messageBox;
if (result == ERROR_SUCCESS) {
messageBox.critical(0, "Success", guid);
} else {
messageBox.critical(0, "Error", QString::number(result));
}
messageBox.setFixedSize(500, 200);
Alternatively, you can use RegGetValueA() instead of using
RegOpenKeyExA()+RegQueryValueExA():
const char *path = "SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001";
const char *valueName = "HwProfileGuid";
char guid[40] = {0};
DWORD size = sizeof(guid);
QMessageBox messageBox;
LSTATUS result = RegGetValueA(HKEY_LOCAL_MACHINE, path, valueName, RRF_RT_REG_SZ, NULL, guid, &size);
if (result == ERROR_SUCCESS) {
messageBox.critical(0, "Success", guid);
} else {
messageBox.critical(0, "Error", QString::number(result));
}
messageBox.setFixedSize(500, 200);
Dont pass Computer\\HKEY_LOCAL_MACHINE to the string. just SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001 Because HKEY_LOCAL_MACHINE are exist in RegOpenKey First Parameter. And dont try to open the value of key just the key RegSetKeyValue() For writing value of key
here the working code:
HKEY hKey;
LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001", 0, KEY_ALL_ACCESS, &hKey);
cout << result << "\n";

C++ How To Convert String YYYYMMDD To TimeStamp

I have a program that gets installed on my laptop to a json file.
I retrieve the date of the installed program from registry and I would like to convert that date to a timestamp.
This is my code :
HKEY hUninstKey = NULL;
HKEY hAppKey = NULL;
WCHAR InstallDate[1024];
WCHAR *sRoot = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
long lResult = ERROR_SUCCESS;
DWORD dwType = KEY_ALL_ACCESS;
DWORD dwBufferSize = 0;
//Open the "Uninstall" key.
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, sRoot, 0, KEY_READ, &hUninstKey) != ERROR_SUCCESS)
{
}
for (DWORD dwIndex = 0; lResult == ERROR_SUCCESS; dwIndex++)
{
//Enumerate all sub keys...
dwBufferSize = sizeof(sAppKeyName);
if ((lResult = RegEnumKeyEx(hUninstKey, dwIndex, sAppKeyName,
&dwBufferSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
{
//Open the sub key.
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, sSubKey, 0, KEY_READ, &hAppKey) != ERROR_SUCCESS)
{
RegCloseKey(hAppKey);
RegCloseKey(hUninstKey);
}
//Get the display name value from the application's sub key.
dwBufferSize = sizeof((RegQueryValueEx(hAppKey, L"InstallDate", NULL, &dwType, (unsigned char*)InstallDate, &dwBufferSize) == ERROR_SUCCESS))
{
// Display Date Of Installed Program
const TCHAR* xInstallDate = (TCHAR*)InstallDate;
char DateInstall[MAX_LENGTH + 200];
wcstombs_s(&nNumCharConverted, DateInstall, MAX_LENGTH + 200,
xInstallDate, MAX_LENGTH + 200);
file << "\"',date='" << DateInstall << endl;
}
else {
//Display name value doe not exist, this application was probably uninstalled.
}
RegCloseKey(hAppKey);
}
}
RegCloseKey(hUninstKey);
Could anyone please help with my code?
You should look into the usage of std::chrono::parse. It converts string streams into chrono objects which represent time.
This is the documentation of std::chrono::parse: https://en.cppreference.com/w/cpp/chrono/parse
This is a relevant question on SO (you are possibly a duplicate): How to parse a date string into a c++11 std::chrono time_point or similar?

Crash Dump generation and analysis issues

I used the example from https://github.com/hdeldar/QtCrashDumper and made us of the same in my application. Lately for actual crashes in the application the stack trace has been a little useless.
Another question is the dmp files generated module version does not match. I have double checked the module version and it is the same however the dump file has different versions. File version is 2.8.0.4 and the dmp file has 2.08.0.4
I have looked into a few answers on stackoverflow to not understand what functions I am missing.
CrashDump Info
The KB link does not exist anymore - KB313109
My implementation
#include "CrashDumpDef.h"
#include "version.h"
#ifdef USE_MINI_DUMP
#include "CrashDump.h"
#include "FileSystem.h"
LPCWSTR CrashDump::m_szAppName;
LPWSTR CrashDump::m_szAppVersion;
LPWSTR CrashDump::m_szAppBuildNumber;
WCHAR CrashDump::m_szMessageText[MAX_WARNING_MESSAGE_PATH];
LPWSTR CrashDump::m_szDumpFilePath;
#define DEFAULT_MESSAGE_TEXT L"%s Designer has experienced an issue. \nCrash dump has been saved in %s."
#define MAX_DUMP_FILE_NUMBER 9999
CrashDump::CrashDump(LPCWSTR szAppName, LPCWSTR szVersion, LPCWSTR szBuildNumber)
{
// if this assert fires then you have two instances of CrashDump
// which is not allowed
Q_ASSERT(m_szAppName == NULL);
const char* sz = VER_PRODUCTVERSION_STR;
std::vector<wchar_t> vec;
size_t len = strlen(sz);
vec.resize(len + 1);
mbstowcs(&vec[0], sz, len);
const wchar_t* productVersion = &vec[0];
std::string version = VER_PRODUCTVERSION_STR;
char build = version.back();
const char* buildNum = new char(build);
std::vector<wchar_t> vec1;
size_t lenA = strlen(buildNum);
vec1.resize(lenA + 1);
mbstowcs(&vec1[0], buildNum, lenA);
const wchar_t* buildNumber = &vec1[0];
m_szAppName = szAppName ? wcsdup(szAppName) : wcsdup(L"Designer");
m_szAppVersion = productVersion ? wcsdup(productVersion) : wcsdup(productVersion);
m_szAppBuildNumber = buildNumber ? wcsdup(buildNumber) : wcsdup(buildNumber);
wcscpy(m_szMessageText, DEFAULT_MESSAGE_TEXT);
m_szDumpFilePath = NULL;
::SetUnhandledExceptionFilter(TopLevelFilter);
}
CrashDump::~CrashDump()
{
}
void CrashDump::SetVersion(LPCWSTR szVersion)
{
if (szVersion)
{
free(m_szAppVersion);
m_szAppVersion = wcsdup(szVersion);
}
}
void CrashDump::SetBuildNumber(LPCWSTR szBuildNumber)
{
if (szBuildNumber)
{
free(m_szAppBuildNumber);
m_szAppBuildNumber = wcsdup(szBuildNumber);
}
}
void CrashDump::SetDumpFilePath(LPCWSTR szFilePath)
{
free(m_szDumpFilePath);
{
m_szDumpFilePath = wcsdup(szFilePath);
}
}
LONG CrashDump::TopLevelFilter(struct _EXCEPTION_POINTERS *pExceptionInfo)
{
//::MessageBoxW(NULL, L"debugging", m_szAppName, MB_OK);
LONG retval = EXCEPTION_CONTINUE_SEARCH;
HWND hParent = NULL; // find a better value for your app
// firstly see if dbghelp.dll is around and has the function we need
// look next to the EXE first, as the one in System32 might be old
// (e.g. Windows 2000)
HMODULE hDll = NULL;
WCHAR szDbgHelpPath[_MAX_PATH];
if (GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH))
{
WCHAR *pSlash = wcsrchr(szDbgHelpPath, L'\\');
if (pSlash)
{
wcscpy(pSlash + 1, L"DBGHELP.DLL");
hDll = ::LoadLibrary(szDbgHelpPath);
}
}
if (hDll == NULL)
{
// load any version we can
hDll = ::LoadLibrary(L"DBGHELP.DLL");
}
LPCWSTR szResult = NULL;
if (hDll)
{
MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");
if (pDump)
{
WCHAR szDumpPath[_MAX_PATH];
WCHAR szDumpRootPath[_MAX_PATH];
WCHAR szScratch[_MAX_PATH];
// work out a good place for the dump file - add the path here
if (m_szDumpFilePath == NULL)
{
if (GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH))
{
WCHAR *pSlash = wcsrchr(szDbgHelpPath, L'\\');
if (pSlash)
{
wcscpy(pSlash + 1, L"");
wcscpy(szDumpPath, szDbgHelpPath);
}
}
//else if (!GetTempPath(_MAX_PATH, szDumpPath))
std::string dmpFile = filesystem::buildFilename(QStringList()
<< QDir::toNativeSeparators(QDir::homePath()) + "\\AppData\\Roaming\\ABC\\logs\\"
).toStdString();
std::wstring wideDmpFile;
for (int i = 0; i < dmpFile.length(); ++i)
wideDmpFile += wchar_t(dmpFile[i]);
const wchar_t* szName = wideDmpFile.c_str();
wcscpy(szDumpPath, szName);
}
else
{
wcscpy(szDumpPath, m_szDumpFilePath);
}
wcscpy(szDumpRootPath, szDumpPath);
//PrintDebug(L"[CrashDump] Mini Dump file:[%s]",szDumpPath);
// ask the user if they want to save a dump file
//if (::MessageBox( NULL, _T("Crash, would you like to save a diagnostic file?"), m_szAppName, MB_YESNO )==IDYES)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
int i = 1;
WCHAR szFileNumber[_MAX_PATH];
while (hFile == INVALID_HANDLE_VALUE)
{
swprintf(szFileNumber, sizeof(szFileNumber), L"_%04d", i);
wcscpy(szDumpPath, szDumpRootPath);
wcscat(szDumpPath, m_szAppName);
wcscat(szDumpPath, L"_");
wcscat(szDumpPath, m_szAppVersion);
wcscat(szDumpPath, L"_");
wcscat(szDumpPath, m_szAppBuildNumber);
wcscat(szDumpPath, szFileNumber);
wcscat(szDumpPath, L".dmp");
hFile = ::CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
i++;
if (i > MAX_DUMP_FILE_NUMBER)
{
hFile = ::CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
break;
}
}
// create the file
if (hFile != INVALID_HANDLE_VALUE)
{
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = NULL;
// write the dump
BOOL bOK = pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL);
if (bOK)
{
swprintf(szScratch, sizeof(szScratch), L"Saved dump file to '%s'", szDumpPath);
szResult = szScratch;
retval = EXCEPTION_EXECUTE_HANDLER;
}
else
{
swprintf(szScratch, sizeof(szScratch), L"Failed to save dump file to '%s' (error %d)", szDumpPath, GetLastError());
szResult = szScratch;
}
::CloseHandle(hFile);
WCHAR csOutMessage[MAX_WARNING_MESSAGE_PATH];
swprintf(csOutMessage, sizeof(csOutMessage), m_szMessageText, m_szAppName, szDumpPath);
//PrintError(_T("%s"), csOutMessage);
::MessageBoxW(NULL, csOutMessage, m_szAppName, MB_OK);
}
else
{
swprintf(szScratch, sizeof(szScratch), L"Failed to create dump file '%s' (error %d)", szDumpPath, GetLastError());
szResult = szScratch;
}
}
}
else
{
szResult = L"DBGHELP.DLL too old";
}
}
else
{
szResult = L"DBGHELP.DLL not found";
}
if (szResult)
{
//PrintDebug(_T("[CrashDump] Mini Dump result:[%s]"),szResult);
}
return retval;
}
#endif

Writing & reading to windows registry with windows API

I am trying to write&read from the windows register:
Writting:
std::string path = "c:\\"
LPCTSTR str_data = TEXT(path.c_str());
auto size = static_cast<DWORD>(strlen(str_data));
LONG setRes = RegSetValueEx(*key, TEXT("DumpFolder"), 0, REG_EXPAND_SZ, (LPBYTE)str_data, size);
Reading:
char str_data[1028];
DWORD keyType;
DWORD size;
auto sk = TEXT("SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps");
auto status = RegGetValue(HKEY_LOCAL_MACHINE, sk, TEXT("DumpFolder"), RF_RT_REG_EXPAND_SZ, &keyType, str_data, &size);
Writing appears to work fine, at least it looks fine in regedit.exe.
Reading fails with ERROR_INVALID_PARAMETER = 87. If I change RF_RT_REG_EXPAND_SZ to RRF_RT_ANY, it works in debug mode, but still fails in release with error code ERROR_MORE_DATA = 234. I tried:
std::string path = "c:\\";
path = path + "\0" (it should be null terminated anyway
but it doesn't help
UPDATE
First of all, thanks for answers, I understand the thing a little better now. Unfortunately, I am still unable to read the string successfully.
Here is the test example combined from the answer below:
HKEY registry_key;
LPCTSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting";
// open registry key
auto openRes = RegOpenKey(HKEY_CURRENT_USER, sk, &registry_key);
// set default dump options
HKEY default_key;
auto createRes = RegCreateKey(registry_key, "LocalDumps", &default_key);
if (createRes != ERROR_SUCCESS) {
auto b = createRes;
}
std::string path = "c:\\";
LONG setRes = RegSetValueExA(default_key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
std::string str_data;
DWORD size = 0;
const char *sak = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
auto status = RegGetValueA(HKEY_CURRENT_USER, sak, "DumpFolder", RRF_RT_REG_EXPAND_SZ, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1)) {
str_data.resize(size - 1);
status = RegGetValueA(HKEY_CURRENT_USER, sk, "DumpFolder", RRF_RT_REG_EXPAND_SZ, NULL, &str_data[0], &size);
}
Writing again works fine (checked in regedit, and the return error code). On the other hand, reading the size of string register sets the size to 0 and returns error code 87 = ERROR_INVALID_PARAMETER.
Apparently, I am still missing something. (the project is set to multy-byte character set)
SOLUTION
After fixing things proposed by the answers below, the following code worked for me:
#include <Windows.h>
#include <string>
#include <iostream>
#define reg_type HKEY_LOCAL_MACHINE
void main() {
const std::string reg_path = "Software\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";
const std::string dump_folder = "DumpFolder";
const std::string path = "c:\\";
// WRITING
HKEY default_key;
auto status = RegCreateKeyExA(reg_type, reg_path.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, NULL, &default_key, NULL);
if (status != ERROR_SUCCESS) {
std::cout << "Creating key failed.";
return;
}
status = RegSetValueExA(default_key, dump_folder.c_str(), 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
if (status != ERROR_SUCCESS) {
std::cout << "Setting key value failed.";
return;
}
// READING
std::string str_data;
DWORD size = 0;
status = RegGetValueA(default_key, "", dump_folder.c_str(), RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1)){
str_data.resize(size - 1);
status = RegGetValueA(default_key, "", dump_folder.c_str(), RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ, NULL, &str_data[0], &size);
std::cout << "Successfully read key value: " << str_data;
} else {
std::cout << "Unable to retrive value. Error: " << status;
}
RegCloseKey(default_key);
}
I found, that RegGetValueA should be called with a
RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ
flag, which appears strange, but is described in header where it is defined, so I guess it is correct. If using only
RRF_RT_REG_EXPAND_SZ
error 87 occurs ERROR_INVALID_PARAMETER.
On the writing side:
std::string uses char elements, but TCHAR maps to either char or wchar_t depending on whether your code is compiled with UNICODE defined or not.
The TEXT() macro only works with compile-time literals, you can't use it with runtime data. TEXT(path.c_str()) is an invalid type-cast, and won't even compile if UNICODE is enabled.
You are clearly working with char data, so you should be using the char-based API functions instead of the TCHAR-based functions.
You are also not following one of the most important rules of RegSetValueEx():
For string-based types, such as REG_SZ, the string must be null-terminated. With the REG_MULTI_SZ data type, the string must be terminated with two null characters... The size of the information pointed to by the lpData parameter, in bytes. If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must include the size of the terminating null character or characters.
std::string::c_str() returns a pointer to null-terminated data, but you are not including the null terminator when reporting the size of the data you are writing to the Registry. RegGetValue() knows how to deal with that mistake, but RegGetValueEx() does not. You might not be the only person to ever read the value, so make sure you include the null terminator properly.
Try this instead:
std::string path = "c:\\";
LONG setRes = RegSetValueExA(*key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size()+1);
On the reading side:
You are getting errors because you are not telling RegGetValue() how large your str_data buffer is. You have to set your size variable to the size of str_data, in bytes, before you pass it in.
Try this instead:
char str_data[1028];
DWORD size = sizeof(str_data);
DWORD dwFlags = RRF_RT_REG_EXPAND_SZ;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps", "DumpFolder", RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND, NULL, str_data, &size);
Alternatively:
std:string str_data;
DWORD size = 0;
const char *sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size-1);
status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
UPDATE: your new code fails because you have introduced new bugs.
You are using legacy Registry functions that are meant for 16bit apps. You need to use RegOpenKeyEx/RegCreateKeyEx instead of RegOpenKey/RegCreateKey, and then you can specify only the specific access rights that you actually need (create subkeys, set values, read values, etc). Even better, RegCreateKeyEx() creates missing keys for you, so you don't need to manually open a parent key separately just to create a new subkey.
Also, you changed HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER, but not consistently. Some of your steps use one root, other steps use the other root. You are not able to read back the value you are writing because you are not reading from the same key you wrote to.
Try this instead:
LPCSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";
HKEY default_key;
auto status = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sk, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &default_key, NULL);
if (status == ERROR_SUCCESS)
{
std::string path = "c:\\";
status = RegSetValueExA(default_key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
RegCloseKey(default_key);
}
LPCSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
std::string str_data;
DWORD size = 0;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size - 1);
status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
On the other hand, when you have to make multiple API calls to read a value (ie, to query the size, then query the data), you should explicitly open the parent key first:
const char *sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
std:string str_data;
HKEY default_key;
auto status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, sk, 0, KEY_QUERY_VALUE, &dumps_key);
if (status == ERROR_SUCCESS)
{
DWORD size = 0;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
status = RegGetValueA(default_key, "", "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size-1);
status = RegGetValueA(default_key, "", "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
RegCloseKey(default_key);
}