66import logging .handlers
77import os
88import shutil
9+ import subprocess
910import sys
1011from pathlib import Path
1112from typing import Dict , Iterator , Optional , Tuple
2324CONFIG_RELOAD_EVERY = 6 # iterations -> ~30 s
2425
2526PROJECT_NAME = "SRR"
27+ PROJECT_DISPLAY_NAME = "Smart Refresh Rate"
2628PROJECT_EXECUTABLE = PROJECT_NAME + ".exe"
29+ UNINSTALL_REG_KEY = rf"Software\Microsoft\Windows\CurrentVersion\Uninstall\{ PROJECT_NAME } "
2730
2831PATH_APPDATA_LOCAL = Path (os .environ ["LOCALAPPDATA" ]).resolve ()
2932PATH_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+
337414async 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
487566if __name__ == "__main__" :
567+ if "--uninstall" in sys .argv :
568+ _uninstall ()
488569 _setup_logging ()
489570 asyncio .run (main ())
0 commit comments