Python’s winreg module (Windows only, also _winreg in Python 2) provides access to the Windows Registry. import winreg. Open: winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\MyApp") → key handle (context manager). Read: winreg.QueryValueEx(key, "Setting") → (data, type_int). Write: winreg.SetValueEx(key, "Setting", 0, winreg.REG_SZ, "value"). Create: winreg.CreateKey(winreg.HKEY_CURRENT_USER, r"Software\MyApp"). Delete value: winreg.DeleteValue(key, "Setting"). Delete key (no subkeys): winreg.DeleteKey(key, "SubkeyName"). Enumerate subkeys: winreg.EnumKey(key, i) — iterate i = 0, 1, … until OSError. Enumerate values: winreg.EnumValue(key, i) → (name, data, type). Key info: winreg.QueryInfoKey(key) → (num_subkeys, num_values, last_write_time). Root hives: HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, HKEY_CLASSES_ROOT, HKEY_USERS, HKEY_CURRENT_CONFIG. Type constants: REG_SZ (string), REG_DWORD (32-bit int), REG_BINARY, REG_EXPAND_SZ (string with env vars), REG_MULTI_SZ (list of strings), REG_QWORD (64-bit int). Claude Code reads installed software paths, environment variables, startup programs, application settings, and Windows system configuration.
CLAUDE.md for winreg
## winreg Stack
- Stdlib: import winreg (Windows only)
- Root: winreg.HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE
- Open: with winreg.OpenKey(root, path) as key: ...
- Read: data, typ = winreg.QueryValueEx(key, "Name")
- Write: winreg.SetValueEx(key, "Name", 0, winreg.REG_SZ, "val")
- Create: with winreg.CreateKey(root, path) as key: ...
- Delete: winreg.DeleteValue(key, "Name")
- winreg.DeleteKey(key, "SubkeyName") # no children
- Enum: winreg.EnumKey(key, i) / EnumValue(key, i)
- Info: winreg.QueryInfoKey(key) → (n_subkeys, n_values, mtime)
winreg Registry Pipeline
# app/winregutil.py — read, write, enumerate, app settings, startup, installer check
from __future__ import annotations
import os
import time
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Any, Iterator
# Only import winreg on Windows
_WINREG_AVAILABLE = False
if os.name == "nt":
try:
import winreg as _wr
_WINREG_AVAILABLE = True
except ImportError:
pass
# ─────────────────────────────────────────────────────────────────────────────
# 1. Low-level open / read / write helpers
# ─────────────────────────────────────────────────────────────────────────────
def _root(hive: str = "HKCU"):
"""Map short hive name to winreg constant."""
if not _WINREG_AVAILABLE:
return None
mapping = {
"HKCU": _wr.HKEY_CURRENT_USER,
"HKLM": _wr.HKEY_LOCAL_MACHINE,
"HKCR": _wr.HKEY_CLASSES_ROOT,
"HKU": _wr.HKEY_USERS,
"HKCC": _wr.HKEY_CURRENT_CONFIG,
}
return mapping.get(hive.upper(), _wr.HKEY_CURRENT_USER)
def read_value(path: str, name: str, hive: str = "HKCU") -> Any:
"""
Read a named value from the registry. Returns None if not found.
Example:
val = read_value(r"Software\\MyApp", "Version")
val = read_value(r"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
"ProductName", hive="HKLM")
"""
if not _WINREG_AVAILABLE:
return None
try:
with _wr.OpenKey(_root(hive), path) as key:
data, _ = _wr.QueryValueEx(key, name)
return data
except OSError:
return None
def write_value(path: str, name: str, data: Any, reg_type: int | None = None,
hive: str = "HKCU") -> bool:
"""
Write a named value to the registry, creating the key if needed.
reg_type defaults to REG_SZ for str, REG_DWORD for int, REG_BINARY for bytes.
Example:
write_value(r"Software\\MyApp", "Version", "1.0.0")
write_value(r"Software\\MyApp", "MaxItems", 100)
write_value(r"Software\\MyApp", "Flag", b"\\x01\\x00")
"""
if not _WINREG_AVAILABLE:
return False
if reg_type is None:
if isinstance(data, str):
reg_type = _wr.REG_SZ
elif isinstance(data, int):
reg_type = _wr.REG_DWORD
elif isinstance(data, bytes):
reg_type = _wr.REG_BINARY
elif isinstance(data, list):
reg_type = _wr.REG_MULTI_SZ
else:
reg_type = _wr.REG_SZ
data = str(data)
try:
with _wr.CreateKey(_root(hive), path) as key:
_wr.SetValueEx(key, name, 0, reg_type, data)
return True
except OSError:
return False
def delete_value(path: str, name: str, hive: str = "HKCU") -> bool:
"""
Delete a named value. Returns True on success, False if not found.
Example:
delete_value(r"Software\\MyApp", "OldSetting")
"""
if not _WINREG_AVAILABLE:
return False
try:
with _wr.OpenKey(_root(hive), path,
access=_wr.KEY_SET_VALUE) as key:
_wr.DeleteValue(key, name)
return True
except OSError:
return False
def key_exists(path: str, hive: str = "HKCU") -> bool:
"""
Return True if the registry key exists.
Example:
if key_exists(r"Software\\MyApp"):
print("key exists")
"""
if not _WINREG_AVAILABLE:
return False
try:
with _wr.OpenKey(_root(hive), path):
return True
except OSError:
return False
# ─────────────────────────────────────────────────────────────────────────────
# 2. Enumeration helpers
# ─────────────────────────────────────────────────────────────────────────────
def list_subkeys(path: str, hive: str = "HKCU") -> list[str]:
"""
Return all subkey names under path.
Example:
subkeys = list_subkeys(r"Software\\MyApp")
"""
if not _WINREG_AVAILABLE:
return []
results: list[str] = []
try:
with _wr.OpenKey(_root(hive), path) as key:
i = 0
while True:
try:
results.append(_wr.EnumKey(key, i))
i += 1
except OSError:
break
except OSError:
pass
return results
@dataclass
class RegValue:
name: str
data: Any
type: int
type_name: str = ""
def __post_init__(self) -> None:
if not self.type_name:
self.type_name = _TYPE_NAMES.get(self.type, f"REG_{self.type}")
_TYPE_NAMES: dict[int, str] = {}
if _WINREG_AVAILABLE:
_TYPE_NAMES = {
_wr.REG_SZ: "REG_SZ",
_wr.REG_DWORD: "REG_DWORD",
_wr.REG_BINARY: "REG_BINARY",
_wr.REG_EXPAND_SZ: "REG_EXPAND_SZ",
_wr.REG_MULTI_SZ: "REG_MULTI_SZ",
_wr.REG_QWORD: "REG_QWORD",
_wr.REG_NONE: "REG_NONE",
}
def list_values(path: str, hive: str = "HKCU") -> list[RegValue]:
"""
Return all named values under path as RegValue objects.
Example:
for v in list_values(r"Software\\MyApp"):
print(v.name, v.type_name, repr(v.data))
"""
if not _WINREG_AVAILABLE:
return []
results: list[RegValue] = []
try:
with _wr.OpenKey(_root(hive), path) as key:
i = 0
while True:
try:
name, data, typ = _wr.EnumValue(key, i)
results.append(RegValue(name=name, data=data, type=typ))
i += 1
except OSError:
break
except OSError:
pass
return results
# ─────────────────────────────────────────────────────────────────────────────
# 3. Application settings store
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class AppSettings:
"""
Persist application settings in the Windows Registry under
HKCU\\Software\\<company>\\<app>.
Example:
cfg = AppSettings("Acme", "MyTool")
cfg.set("theme", "dark")
cfg.set("window_width", 1024)
print(cfg.get("theme")) # "dark"
print(cfg.get("missing", "N/A")) # "N/A"
print(cfg.all())
"""
company: str
app: str
hive: str = "HKCU"
@property
def _path(self) -> str:
return rf"Software\{self.company}\{self.app}"
def get(self, name: str, default: Any = None) -> Any:
val = read_value(self._path, name, self.hive)
return val if val is not None else default
def set(self, name: str, data: Any, reg_type: int | None = None) -> bool:
return write_value(self._path, name, data, reg_type, self.hive)
def delete(self, name: str) -> bool:
return delete_value(self._path, name, self.hive)
def all(self) -> dict[str, Any]:
return {v.name: v.data for v in list_values(self._path, self.hive)}
def exists(self) -> bool:
return key_exists(self._path, self.hive)
# ─────────────────────────────────────────────────────────────────────────────
# 4. Installed software inventory
# ─────────────────────────────────────────────────────────────────────────────
_UNINSTALL_PATH = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
_UNINSTALL_PATH_32 = r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
@dataclass
class InstalledApp:
name: str
version: str = ""
publisher: str = ""
install_dir: str = ""
uninstall: str = ""
def list_installed(include_32bit: bool = True) -> list[InstalledApp]:
"""
Return a list of installed applications by reading the registry
Uninstall keys for both 64-bit and (optionally) 32-bit apps.
Example:
apps = list_installed()
for app in apps:
print(app.name, app.version)
"""
if not _WINREG_AVAILABLE:
return []
results: list[InstalledApp] = []
paths = [(_UNINSTALL_PATH, "HKLM")]
if include_32bit:
paths.append((_UNINSTALL_PATH_32, "HKLM"))
seen: set[str] = set()
for base_path, hive in paths:
for subkey_name in list_subkeys(base_path, hive):
full_path = rf"{base_path}\{subkey_name}"
vals = {v.name: v.data for v in list_values(full_path, hive)}
name = vals.get("DisplayName", "")
if not name or name in seen:
continue
seen.add(name)
results.append(InstalledApp(
name=name,
version=vals.get("DisplayVersion", ""),
publisher=vals.get("Publisher", ""),
install_dir=vals.get("InstallLocation", ""),
uninstall=vals.get("UninstallString", ""),
))
results.sort(key=lambda a: a.name.lower())
return results
# ─────────────────────────────────────────────────────────────────────────────
# 5. Startup entry manager
# ─────────────────────────────────────────────────────────────────────────────
_RUN_PATH = r"Software\Microsoft\Windows\CurrentVersion\Run"
def list_startup_entries() -> dict[str, str]:
"""
Return {name: command} for all HKCU Run entries (current user startup).
Example:
for name, cmd in list_startup_entries().items():
print(name, "→", cmd)
"""
return {v.name: v.data for v in list_values(_RUN_PATH, "HKCU")}
def add_startup_entry(name: str, command: str) -> bool:
"""
Add or update a startup entry in HKCU Run.
Example:
add_startup_entry("MyApp", r'C:\\Program Files\\MyApp\\myapp.exe --silent')
"""
return write_value(_RUN_PATH, name, command, hive="HKCU")
def remove_startup_entry(name: str) -> bool:
"""Remove a startup entry. Returns True on success."""
return delete_value(_RUN_PATH, name, hive="HKCU")
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== winreg demo ===")
print(f" Windows available: {_WINREG_AVAILABLE}")
if not _WINREG_AVAILABLE:
print(" (non-Windows: showing stub results only)")
# ── AppSettings ───────────────────────────────────────────────────────────
print("\n--- AppSettings ---")
cfg = AppSettings("ClaudeSkills360Demo", "TestApp")
if _WINREG_AVAILABLE:
cfg.set("theme", "dark")
cfg.set("max_items", 42)
cfg.set("tags", ["python", "windows", "registry"])
print(f" theme = {cfg.get('theme')!r}")
print(f" max_items = {cfg.get('max_items')!r}")
print(f" tags = {cfg.get('tags')!r}")
print(f" missing = {cfg.get('missing', 'N/A')!r}")
print(f" all keys = {list(cfg.all().keys())}")
# clean up demo key
cfg.delete("theme")
cfg.delete("max_items")
cfg.delete("tags")
else:
print(" (skipped — Windows only)")
# ── read Windows ProductName ───────────────────────────────────────────────
print("\n--- read HKLM ProductName ---")
product = read_value(
r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
"ProductName",
hive="HKLM",
)
print(f" ProductName = {product!r}")
# ── list_installed (first 5) ───────────────────────────────────────────────
print("\n--- list_installed (first 5) ---")
apps = list_installed()
for app in apps[:5]:
print(f" {app.name[:40]:40s} {app.version}")
print(f" ... ({len(apps)} total)")
# ── startup entries ────────────────────────────────────────────────────────
print("\n--- startup entries (HKCU Run) ---")
entries = list_startup_entries()
if entries:
for name, cmd in list(entries.items())[:3]:
print(f" {name[:30]:30s} {cmd[:50]}")
else:
print(" (no startup entries found)")
print("\n=== done ===")
For the regedit / reg.exe subprocess alternative — subprocess.run(["reg", "query", r"HKCU\Software\MyApp", "/v", "Setting"], capture_output=True) lets you call the Windows reg.exe command-line tool — use reg.exe via subprocess for one-off scripts or when you need to avoid the winreg import; use winreg directly when you need structured data types (REG_DWORD, REG_MULTI_SZ), enumeration, or performance in loops. For the winreg + ctypes advanced alternative — ctypes.windll.advapi32.RegNotifyChangeKeyValue(...) provides asynchronous registry change notification events that winreg does not expose — use ctypes + advapi32 directly when you need to watch a key for changes in real time; use winreg for all standard read/write/enumerate operations. The Claude Skills 360 bundle includes winreg skill sets covering read_value()/write_value()/delete_value()/key_exists() primitives, list_subkeys()/list_values()/RegValue dataclass enumerators, AppSettings persistent settings store, list_installed()/InstalledApp software inventory, and list_startup_entries()/add_startup_entry()/remove_startup_entry() Run-key manager. Start with the free tier to try Windows Registry patterns and winreg pipeline code generation.