Skip to content

Commit c8c2497

Browse files
committed
Implement uninstall functionality and create start menu shortcut
1 parent 1264d16 commit c8c2497

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

main.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import logging.handlers
77
import os
88
import shutil
9+
import subprocess
910
import sys
1011
from pathlib import Path
1112
from typing import Dict, Iterator, Optional, Tuple
@@ -23,7 +24,9 @@
2324
CONFIG_RELOAD_EVERY = 6 # iterations -> ~30 s
2425

2526
PROJECT_NAME = "SRR"
27+
PROJECT_DISPLAY_NAME = "Smart Refresh Rate"
2628
PROJECT_EXECUTABLE = PROJECT_NAME + ".exe"
29+
UNINSTALL_REG_KEY = rf"Software\Microsoft\Windows\CurrentVersion\Uninstall\{PROJECT_NAME}"
2730

2831
PATH_APPDATA_LOCAL = Path(os.environ["LOCALAPPDATA"]).resolve()
2932
PATH_TO_PROGRAM = PATH_APPDATA_LOCAL / PROJECT_NAME
@@ -334,6 +337,80 @@ async def get_processes(app_name: str):
334337
return out
335338

336339

340+
def _register_uninstall(target_exe: Path) -> None:
341+
try:
342+
import winreg
343+
uninstall_cmd = f'"{target_exe}" --uninstall'
344+
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, UNINSTALL_REG_KEY) as key:
345+
winreg.SetValueEx(key, "DisplayName", 0, winreg.REG_SZ, PROJECT_DISPLAY_NAME)
346+
winreg.SetValueEx(key, "UninstallString", 0, winreg.REG_SZ, uninstall_cmd)
347+
winreg.SetValueEx(key, "DisplayIcon", 0, winreg.REG_SZ, f'"{target_exe}",0')
348+
winreg.SetValueEx(key, "InstallLocation", 0, winreg.REG_SZ, str(target_exe.parent))
349+
winreg.SetValueEx(key, "NoModify", 0, winreg.REG_DWORD, 1)
350+
winreg.SetValueEx(key, "NoRepair", 0, winreg.REG_DWORD, 1)
351+
logging.info("uninstall registry entry registered")
352+
except Exception as e:
353+
logging.warning(f"_register_uninstall failed: {e}")
354+
355+
356+
def _uninstall() -> None:
357+
target_exe = PATH_TO_PROGRAM / PROJECT_EXECUTABLE
358+
359+
for p in psutil.process_iter(["pid", "name", "exe"]):
360+
try:
361+
if p.info["name"] == PROJECT_EXECUTABLE and p.pid != os.getpid():
362+
p.kill()
363+
except (psutil.NoSuchProcess, psutil.AccessDenied):
364+
pass
365+
366+
autostart.disable(PROJECT_NAME)
367+
368+
start_menu = Path(os.environ["APPDATA"]) / "Microsoft" / "Windows" / "Start Menu" / "Programs"
369+
shortcut = start_menu / f"{PROJECT_NAME}.lnk"
370+
try:
371+
shortcut.unlink(missing_ok=True)
372+
except Exception as e:
373+
logging.warning(f"shortcut removal failed: {e}")
374+
375+
try:
376+
import winreg
377+
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, UNINSTALL_REG_KEY)
378+
except Exception as e:
379+
logging.warning(f"uninstall key removal failed: {e}")
380+
381+
# Delete install dir after process exits — PowerShell runs detached
382+
ps_cmd = f'Start-Sleep -Seconds 2; Remove-Item -Recurse -Force "{PATH_TO_PROGRAM}"'
383+
subprocess.Popen(
384+
["powershell", "-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", ps_cmd],
385+
creationflags=subprocess.CREATE_NO_WINDOW,
386+
)
387+
388+
ctypes.windll.user32.MessageBoxW(
389+
None, f"{PROJECT_DISPLAY_NAME} has been uninstalled.", PROJECT_DISPLAY_NAME, 0x40
390+
)
391+
sys.exit(0)
392+
393+
394+
def _create_start_menu_shortcut(target_exe: Path) -> None:
395+
start_menu = Path(os.environ["APPDATA"]) / "Microsoft" / "Windows" / "Start Menu" / "Programs"
396+
shortcut = start_menu / f"{PROJECT_NAME}.lnk"
397+
script = (
398+
f'$s=(New-Object -COM WScript.Shell).CreateShortcut("{shortcut}");'
399+
f'$s.TargetPath="{target_exe}";'
400+
f'$s.WorkingDirectory="{target_exe.parent}";'
401+
f'$s.Description="Smart Refresh Rate";'
402+
f'$s.Save()'
403+
)
404+
try:
405+
subprocess.run(
406+
["powershell", "-NoProfile", "-NonInteractive", "-Command", script],
407+
capture_output=True, timeout=10,
408+
)
409+
logging.info(f"start menu shortcut created: {shortcut}")
410+
except Exception as e:
411+
logging.warning(f"start menu shortcut failed: {e}")
412+
413+
337414
async def install():
338415
"""Copy exe into %LOCALAPPDATA%\\SRR, register autostart, restart from there."""
339416
if PATH_BASE_DIR == PATH_TO_PROGRAM:
@@ -359,6 +436,8 @@ async def install():
359436
raise
360437

361438
autostart.enable(PROJECT_NAME, target_exe)
439+
_register_uninstall(target_exe)
440+
_create_start_menu_shortcut(target_exe)
362441

363442
try:
364443
os.startfile(str(target_exe))
@@ -485,5 +564,7 @@ def _setup_logging():
485564

486565

487566
if __name__ == "__main__":
567+
if "--uninstall" in sys.argv:
568+
_uninstall()
488569
_setup_logging()
489570
asyncio.run(main())

0 commit comments

Comments
 (0)