Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save aont/b223b1c43b99784fe23a0de35793caed to your computer and use it in GitHub Desktop.

Select an option

Save aont/b223b1c43b99784fe23a0de35793caed to your computer and use it in GitHub Desktop.

Reading Windows Environment Variables in Python Without Uppercasing

On Windows, os.environ in Python converts all environment variable names to uppercase. This is normally fine because Windows treats environment variable names as case-insensitive, but it is a problem if you want to see the original casing.

To keep the original case, you can read the environment variables directly from the Windows API using ctypes and GetEnvironmentStringsW.

With this function, the keys in env use the same casing as in the Windows environment block, instead of being forced to uppercase like os.environ on Windows.

import ctypes
from ctypes import wintypes
# Pointer type for wchar_t*
LPWCH = ctypes.POINTER(ctypes.c_wchar)
# Load kernel32.dll
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
GetEnvironmentStringsW = kernel32.GetEnvironmentStringsW
FreeEnvironmentStringsW = kernel32.FreeEnvironmentStringsW
# Set return / argument types to match wchar_t*
GetEnvironmentStringsW.restype = LPWCH
FreeEnvironmentStringsW.argtypes = [LPWCH]
def get_env_block():
lpEnv = GetEnvironmentStringsW()
if not lpEnv:
raise ctypes.WinError(ctypes.get_last_error())
env = {}
try:
# lpEnv is a continuous wide-character string like:
# "VAR=VALUE\0VAR2=VALUE2\0\0"
# Instead of iterating over the pointer itself, iterate using its address (int).
addr = ctypes.addressof(lpEnv.contents)
wchar_size = ctypes.sizeof(ctypes.c_wchar)
while True:
# Read a null-terminated wide string from the current address
s = ctypes.wstring_at(addr)
if not s:
# Encountering an empty string means just before the double terminator — end here
break
# Skip internal entries that start with "="
if "=" in s and not s.startswith("="):
name, value = s.split("=", 1)
env[name] = value
# Advance the pointer (in bytes) by (length + null terminator) in wchar units
addr += (len(s) + 1) * wchar_size
return env
finally:
if lpEnv:
FreeEnvironmentStringsW(lpEnv)
if __name__ == "__main__":
env = get_env_block()
# Print all variables one by one
for k, v in env.items():
print(f"{k}={v}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment