Reputation: 64
I'll just start off by saying that I'm by no means an expert in C++, so any pointers/tips are greatly appreciated. I'm having some difficulties reading and writing from registry, while keeping variables, i.e. not expanding them. I'm trying to append my executable path to the PATH environment variable (permanently), but I'm running into all sorts of problems. I have a long PATH variable that makes it impossible to edit without using a program or regedit, so I opted to create an "OldPath" variable with my current PATH variable, and change my PATH variable to %OldPath%. This has worked great, but now when I try to write to it with C++, %OldPath% gets expanded into the old path variable and as a result, the variable gets truncated.
I tried first with normal strings, but I ended up with what looked like Chinese symbols in my PATH variable, so I changed it to wstring. Now I get normal strings, but the string gets truncated at 1172 characters.
My desired end result is that PATH is set to %OldPath;<current_path>
get_path_env()
inline std::wstring get_path_env()
{
wchar_t* buf = nullptr;
size_t sz = 0;
if (_wdupenv_s(&buf, &sz, L"PATH") == 0 && buf != nullptr)
{
std::wstring path_env = buf;
free(buf);
return path_env;
}
return L"";
}
set_permanent_environment_variable()
inline bool set_permanent_environment_variable()
{
const std::wstring path_env = get_path_env();
if (path_env == L"")
{
return false;
}
std::wstringstream wss;
wss << path_env;
if (path_env.back() != ';')
{
wss << L';';
}
wss << std::filesystem::current_path().wstring() << L'\0';
const std::wstring temp_data = wss.str();
HKEY h_key;
const auto key_path = TEXT(R"(System\CurrentControlSet\Control\Session Manager\Environment)");
if (const auto l_open_status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_path, 0, KEY_ALL_ACCESS, &h_key); l_open_status == ERROR_SUCCESS)
{
const auto data = temp_data.c_str();
const DWORD data_size = static_cast<DWORD>(lstrlenW(data) + 1);
// ReSharper disable once CppCStyleCast
const auto l_set_status = RegSetValueExW(h_key, L"PATH", 0, REG_EXPAND_SZ, (LPBYTE)data, data_size);
RegCloseKey(h_key);
if (l_set_status == ERROR_SUCCESS)
{
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>("Environment"), SMTO_BLOCK, 100, nullptr);
return true;
}
}
return false;
}
In other words, I want to find the equivalent of the following in C#:
var assemblyPath = Directory.GetParent(Assembly.GetEntryAssembly()!.Location).FullName;
var pathVariable = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("PATH", $"{pathVariable};{assemblyPath}", EnvironmentVariableTarget.Machine);
EDIT: I actually haven't tested if that code expands the value or not, but I want to do as the C# code states and if possible, not expand the variables in the path variable.
Upvotes: 0
Views: 255
Reputation: 58898
You are trying to change the PATH setting in the registry. So one would expect that you would get the current PATH setting from the registry, change it, and set the new PATH setting in the registry.
But you are not getting the PATH setting from the registry. You are getting the PATH variable from the environment instead. Why is that? The environment is controlled by the setting in the registry, but it's not that setting. In particular, you noticed that the environment variables set in the registry get expanded before they actually go into the environment.
It's like changing the wallpaper by taking a screenshot of the desktop, changing the screenshot, then setting it as the wallpaper, then asking how to remove the icons from the wallpaper.
The solution is to simply get the current unexpanded PATH setting from the registry instead of the expanded one from the environment.
Upvotes: 1