Modifying the windows registry can be complicated, generally the documentation available on the topic is less than ideal, hence this tutorial. In the video I am broudly discussing using python to update the windows registry using a simple pattern.
Some terms to help you navigate the post
See my Practical Windows Registry Explanation video for a more in depth look, but here is the terminology you should be aware of:
In particular we are looking at updating the PATH variable on windows, which is stored in the registry. As (essentially) a string of folder paths.
Below is a step-by-step breakdown of the solution to the problem. See the corresponding files in this repo for the full code version.
Before the soltion we need to do some setup for the file by importing our modules, and setting up the folder path we want to add to our PATH variable:
import winreg # Allows access to the windows registry
import ctypes # Allows interface with low-level C API's
program_path = f"%USERPROFILE%\\Documents" # File path to the folder containing our desired executable
Much like you would open a file, you need to initialize your connection to the registry by accessing a Hkey. In our case we want HKEY_CURRENT_USER
. Luckily this hkey is a built in variable in winreg (winreg.HKEY_CURRENT_USER
), so lets connect using ConnectRegistry()
and our hkey:
import winreg # Allows access to the windows registry
import ctypes # Allows interface with low-level C API's
program_path = f"%USERPROFILE%\\Documents" # File path to the folder containing our desired executable
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as hkey: # Get the necessary HKEY
Now that we're in the Hkey we need, lets get into the sub-key we want. In this case it's the Environment
key. Much like our hkey, we need to "open" it:
import winreg # Allows access to the windows registry
import ctypes # Allows interface with low-level C API's
program_path = f"%USERPROFILE%\\Documents"
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as hkey: # Get the necessary HKEY
with winreg.OpenKey(hkey, "Environment", 0, winreg.KEY_ALL_ACCESS) as sub_key: # Go to the environment key
There are a few new parameters we need to contend with on this step, namely key
, reserved
, and access
. In this like key
refers to our open hkey, reserved
should always be 0, and access
refers to winreg's access constants (link to docs in references)
Next we can access the current path value. To do this we can use winreg.EnumValue():
import winreg # Allows access to the windows registry
import ctypes # Allows interface with low-level C API's
program_path = f"%USERPROFILE%\\Documents"
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as hkey: # Get the necessary HKEY
with winreg.OpenKey(hkey, "Environment", 0, winreg.KEY_ALL_ACCESS) as sub_key: # Go to the environment key
existing_path_value = winreg.EnumValue(sub_key, 3)[1] # Grab the current path value
winreg.EnumValue()
returns a tuple for a given value of (name
, data
, type
) where the type is represented by an int. So notice I selected the 3rdth index and took the 1sth index of the resulting tuple (which is the data). Keep in mind This is a cheap solution, for some people the PATH variable will be at a different index, I would recommend iterating through the subkeys and searching for the right name to get the index as a general solution.
Now that we have the current path value in existing_path_value
we need to update it with our new program path, and a semicolon to indicate termination:
import winreg # Allows access to the windows registry
import ctypes # Allows interface with low-level C API's
program_path = f"%USERPROFILE%\\Documents"
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as hkey: # Get the necessary HKEY
with winreg.OpenKey(hkey, "Environment", 0, winreg.KEY_ALL_ACCESS) as sub_key: # Go to the environment key
existing_path_value = winreg.EnumValue(sub_key, 3)[1] # Grab the current path value
new_path_value = existing_path_value + program_path + ";" # Takes the current path value and appends the new program path
Now that we have our new value stored in new_path_value
we need to actually update the registry. This can be done through winreg.SetValueEx()
:
import winreg # Allows access to the windows registry
import ctypes # Allows interface with low-level C API's
program_path = f"%USERPROFILE%\\Documents"
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as hkey: # Get the necessary HKEY
with winreg.OpenKey(hkey, "Environment", 0, winreg.KEY_ALL_ACCESS) as sub_key: # Go to the environment key
existing_path_value = winreg.EnumValue(sub_key, 3)[1] # Grab the current path value
new_path_value = existing_path_value + program_path + ";" # Takes the current path value and appends the new program path
winreg.SetValueEx(sub_key, "PATH", 0, winreg.REG_EXPAND_SZ, new_path_value) # Updated the path with the updated path
For parameters we need to pass the open subkey, the name of the value to update, reverved (always 0), the value type (see references for details), and the new data for the value (new_path_value
in our case).
BUT WAIT, if you run the code nothing changes. There is one more step to get it to work.
I am not going to explain this code, the only thing that matters is the 4th parameter on the last line. Update that param to your sub-key you modified, otherwise just copy pase the new code:
import winreg # Allows access to the windows registry
import ctypes # Allows interface with low-level C API's
program_path = f"%USERPROFILE%\\Documents"
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as hkey: # Get the necessary HKEY
with winreg.OpenKey(hkey, "Environment", 0, winreg.KEY_ALL_ACCESS) as sub_key: # Go to the environment key
existing_path_value = winreg.EnumValue(sub_key, 3)[1] # Grab the current path value
print(f"{existing_path_value=}")
new_path_value = existing_path_value + program_path + ";" # Takes the current path value and appends the new program path
print(f"{new_path_value=}")
winreg.SetValueEx(sub_key, "PATH", 0, winreg.REG_EXPAND_SZ, new_path_value) # Updated the path with the updated path
# Tell other processes to update their environment
HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x1A
SMTO_ABORTIFHUNG = 0x0002
result = ctypes.c_long()
SendMessageTimeoutW = ctypes.windll.user32.SendMessageTimeoutW
SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, u"Environment", SMTO_ABORTIFHUNG, 5000, ctypes.byref(result),)
This code essentially says "Hey, the registry has been updated. You mind saving the changes please".