I am writing a python program that needs to store a value in a persistent user environment variable. I believe these values are stored in the registry. I have tried something like this using the winreg python module.
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', access=winreg.KEY_WRITE)
winreg.SetValueEx(key, envVarName, 0, winreg.REG_SZ, envVarValue)
winreg.Close(key)
I can see from using Registry Editor that this value is successfully set. But if I try and read the value from another python script or from a new powershell instance, I don't see the new value until the machine is rebooted.
What else do I need to do to make this value available to other processes without rebooting?
Looking at this answer gives a possible answer:
https://stackoverflow.com/a/61757725/18300067
os.system("SETX {0} {1} /M".format("start", "test"))
This is how we do it in production on one of Windows servers (code is simplified):
import winreg
regdir = "Environment-test"
keyname = "Name-test"
keyvalue = "Value-test"
def setRegistry(regdir, keyname, keyvalue):
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, regdir) as _:
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, regdir, 0, winreg.KEY_WRITE) as writeRegistryDir:
winreg.SetValueEx(writeRegistryDir, keyname, 0, winreg.REG_SZ, keyvalue)
def getRegistry(regdir, keyname):
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, regdir) as accessRegistryDir:
value, _ = winreg.QueryValueEx(accessRegistryDir, keyname)
return(value)
If I firstly set the value using:
setRegistry(regdir, keyname, keyvalue)
Then check if it's created:
Now I use the getRegistry and open a PS in admin mode to run it:
print(getRegistry(regdir, keyname))
Related
Following solutions (actually it is only one) doesn't work to me :
How to get a name of default browser using python
How to get name of the default browser in windows using python?
Solution was:
from _winreg import HKEY_CURRENT_USER, OpenKey, QueryValue
# In Py3, this module is called winreg without the underscore
with OpenKey(HKEY_CURRENT_USER,
r"Software\Classes\http\shell\open\command") as key:
cmd = QueryValue(key, None)
But unfortunately, in Windows 10 Pro I don't have targeted registry value. I've tried to find alternative keys in Regedit, but no luck.
Please take a look, what my registry virtually contains:
The following works for me on Windows 10 pro:
from winreg import HKEY_CURRENT_USER, OpenKey, QueryValueEx
reg_path = r'Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice'
with OpenKey(HKEY_CURRENT_USER, reg_path) as key:
print(QueryValueEx(key, 'ProgId'))
Result (first with Chrome set as default, then with IE):
$ python test.py
('ChromeHTML', 1)
$ python test.py
('IE.HTTPS', 1)
Please check for the key in windows 10
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\URLAssociations(http|https)\UserChoice
def get_windows_default_browser_launch():
""" On windows, return the default browser for 'https' urls
returns: example '"C:\Program Files\Mozilla Firefox\firefox.exe" -osint -url "%1"'
"""
import winreg
key = winreg.OpenKey(winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER), r"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice")
prog_id, _ = winreg.QueryValueEx(key, "ProgId")
key = winreg.OpenKey(winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE), r"SOFTWARE\Classes\{}\shell\open\command".format(prog_id))
launch_string, _ = winreg.QueryValueEx(key, "") # read the default value
return launch_string
Windows 10 Python3 , may want to change the key for 'http' not https, but this is my code verbatim as my context is of a secured server. I wanted the browser binary name and path, which is just one more line.
To set an environment variable using Windows Command Processor ( cmd) :
SET MY_VARIABLE=c:\path\to\filename.txt
MY_VARIABLE now can be accessed by Python application started by same cmd window:
import os
variable = os.getenv('MY_VARIABLE')
I wonder if there is a way to set an environment variable from inside of Python so it becomes available to other processes running on the same machine?
To set a new environment variable:
os.environ['NEW_VARIABLE'] = 'NEW VALUE'
But this NEW_VARIABLE is lost as soon Python process and exited.
You can store environment variables persistently in the Windows registry. Variables can be stored for the current user, or for the system:
Code to persistently set an environment variable on Windows:
import win32con
import win32gui
try:
import _winreg as winreg
except ImportError:
# this has been renamed in python 3
import winreg
def set_environment_variable(variable, value, user_env=True):
if user_env:
# This is for the user's environment variables
reg_key = winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
'Environment', 0, winreg.KEY_SET_VALUE)
else:
# This is for the system environment variables
reg_key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
0, winreg.KEY_SET_VALUE)
if '%' in value:
var_type = winreg.REG_EXPAND_SZ
else:
var_type = winreg.REG_SZ
with reg_key:
winreg.SetValueEx(reg_key, variable, 0, var_type, value)
# notify about environment change
win32gui.SendMessageTimeout(
win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0,
'Environment', win32con.SMTO_ABORTIFHUNG, 1000)
Test code to invoke above:
set_environment_variable('NEW_VARIABLE', 'NEW VALUE')
Would a simple if not perhaps slightly crude way of doing this be simply to use os.system and pass the command through that as if you were running it in CMD?
An example being os.system("SET MY_VARIABLE=c:\path\to\filename.txt")
Hope that helps.`
I am trying to get all installed programs of my windows computer, therefore I read out the registry.
But somehow python reads the 32bit programs out twice (even though I give him another registry entry)
Here is the code snipped:
def get_programs(registry):
reg = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
programList = []
key = OpenKey(reg, registry)
print(QueryInfoKey(key))
for i in range(0, QueryInfoKey(key)[0]):
programList.append(EnumKey(key, i))
CloseKey(key)
CloseKey(reg)
return programList
I call this function like this:
registry32bit = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
registry64bit = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
programs32bit = get_programs(registry32bit)
programs64bit = get_programs(registry64bit)
Why does python open and read out the same registry (for 32 bit) twice and return the exactly same list?
This appears to work and uses #eryksun suggestion in a comment below about just letting the redirection happen and not explicitly referencing the Wow6432Node registry key. The central idea is to just specify either the KEY_WOW64_32KEY or KEY_WOW64_64KEY flag when opening the uninstall subkey and let the magic happen.
Note: I also Pythonized the code in the get_programs() function some. This made it shorter and more readable in my opinion.
import sys
from _winreg import *
# Assure registry handle objects with context manager protocol implemented.
if sys.version_info.major*10 + sys.version_info.minor < 26:
raise AssertionError('At least Python 2.6 is required.')
def get_programs(subkey, regBitView):
with ConnectRegistry(None, HKEY_LOCAL_MACHINE) as hive:
with OpenKey(hive, subkey, 0, regBitView | KEY_READ) as key:
return [EnumKey(key, i) for i in range(QueryInfoKey(key)[0])]
UNINSTALL_REG_KEY = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
programs32bit = get_programs(UNINSTALL_REG_KEY, KEY_WOW64_32KEY)
programs64bit = get_programs(UNINSTALL_REG_KEY, KEY_WOW64_64KEY)
print('32-bit programs:\n{}'.format(programs32bit))
print('')
print('64-bit programs:\n{}'.format(programs64bit))
Many thanks to #eryksun for the clues and many implementation strategy suggestions.
First thank you for your time reading this question.
I start an app executable (lets call it MyApp.exe) successfully from my windows service under the Interactive user using the following code:
#MyWindowsService.py
console_session_id = win32ts.WTSGetActiveConsoleSessionId()
console_user_token = win32ts.WTSQueryUserToken(console_session_id)
my_app_path= os.path.normpath(r"/Path\to\MyApp.exe")
startup = win32process.STARTUPINFO()
priority = win32con.NORMAL_PRIORITY_CLASS
handle, thread_id ,pid, tid = win32process.CreateProcessAsUser(console_user_token, my_app_path, None, None, None, True, priority, None, None, startup)
From inside MyApp I need to get the environment paths that belong to the interactive user. For example I use the following code to get the path for user's %appdata%:
#MyApp.py
user_app_data_path = os.getenv('APPDATA')
It should return:
C:\Users\ Interactive-user-name \AppData\Roaming
However the returned value is:
C:\Windows\System32\config\systemprofile\AppData\Roaming
Which means although MyApp.exe is started under interactive user's name, it gets the environment for SYSTEM user that the windows service is running under.
My question is how I can get the environment paths that belong to the user not system.
Many thank,
P.S. I am using py2exe to convert MyApp and Windows service into executables.
I found the answer, in case any one is interested:
According to this if the environment is not explicitly specified, the process inherits the environment of the parent. Using this one can get the environment and pass it on to CreateProcessAsUser.
Simply follow the environment word in the following code.
#MyWindowsService.py
console_session_id = win32ts.WTSGetActiveConsoleSessionId()
console_user_token = win32ts.WTSQueryUserToken(console_session_id)
my_app_path= os.path.normpath(r"/Path\to\MyApp.exe")
startup = win32process.STARTUPINFO()
priority = win32con.NORMAL_PRIORITY_CLASS
environment = win32profile.CreateEnvironmentBlock(console_user_token, False)
handle, thread_id ,pid, tid = win32process.CreateProcessAsUser(console_user_token, my_app_path, None, None, None, True, priority, environment, None, startup)
I am trying to trigger an event every time a registry value is being modified.
import win32api
import win32event
import win32con
import _winreg
key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,'Control Panel\Desktop',0,_winreg.KEY_READ)
sub_key = _winreg.CreateKey(key,'Wallpaper')
evt = win32event.CreateEvent(None,0,0,None)
win32api.RegNotifyChangeKeyValue(sub_key,1,win32api.REG_NOTIFY_CHANGE_ATTRIBUTES,evt,True)
ret_code=win32event.WaitForSingleObject(evt,3000)
if ret_code == win32con.WAIT_OBJECT_0:
print "CHANGED"
if ret_code == win32con.WAIT_TIMEOUT:
print "TIMED"
my problem is that this is never triggered , the event always time-out.
(the reg key I am trying to follow is the wallpaper)
[
please note I trigger the event by 1) manually changing the registry value in regedit 2) an automated script which run this :
from ctypes import windll
from win32con import *
windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0,"C:\wall.jpg",SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE)
]
Thanks for any help in advance :)
EDIT:: sorry about formatting
"WallPaper" is a value not a key/subkey. So if you bring up regedit.exe, you'll notice that you've created a new key "HKCU\Control Panel\Desktop\WallPaper" which is distinct from the "WallPaper" value under the "HKCU\Control Panel\Desktop" key.
Here's one way to modify your code to listen for changes:
key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Control Panel\Desktop', 0, _winreg.KEY_READ)
evt = win32event.CreateEvent(None, 0, 0, None)
win32api.RegNotifyChangeKeyValue(key, 1, win32api.REG_NOTIFY_CHANGE_LAST_SET, evt, True)
Note that we don't use a WallPaper subkey any more and note that the "notify fitler" has been changed to NOTIFY_CHANGE_LAST_SET; from the docs this will:
Notify the caller of changes to a value of the key. This can include adding or deleting a value, or changing an existing value.
The rest of your code will work, but you'll need to use the QueryValueEx function before and after to ensure it was the WallPaper value that changed and not some other. (I don't know of a way to listen for specific values.)