11import ctypes
2+ import datetime
3+ import os .path
4+ import sys
5+ import textwrap
26
7+ import inject
38import win32con
4- import win32gui
59import win32console
10+ import win32gui
11+ import win32security
12+
13+ import _meta
14+
15+ _shell = ctypes .windll .shell32
616
717
818class Console :
@@ -19,7 +29,7 @@ def visible(self, value):
1929 self ._visible = value
2030 win32gui .ShowWindow (
2131 self .handle ,
22- win32con . SW_SHOW if value else win32con . SW_HIDE
32+ self . get_visibility_code ( value )
2333 )
2434
2535 def hide (self ):
@@ -28,6 +38,107 @@ def hide(self):
2838 def show (self ):
2939 self .visible = True
3040
41+ @staticmethod
42+ def get_visibility_code (show_console : bool ):
43+ return win32con .SW_SHOW if show_console else win32con .SW_HIDE
44+
45+
46+ def has_admin_rights ():
47+ return _shell .IsUserAnAdmin ()
48+
49+
50+ class StartupTaskGenerator :
51+ def __init__ (self ,):
52+ self .script_path = str (_meta .APP_PATH )
53+ self .task_name = _meta .__product_name__ + "_Startup"
54+ self .template_path = os .path .abspath ("StartupTaskTemplate.xml" )
55+ self .result_path = os .path .abspath ("StartupTask.xml" )
56+
57+ def build_task (self ):
58+ encoding = 'utf-16le'
59+ with open (self .template_path , encoding = encoding ) as f :
60+ template_content = f .read ()
61+ template_content = template_content .format (
62+ script_path = self .script_path ,
63+ user_id = self .user_id ,
64+ author = self .author ,
65+ task_name = self .task_name ,
66+ date_now = self .date_now ,
67+ description = self .description
68+ )
69+ with open (self .result_path , 'w' , encoding = encoding ) as f :
70+ f .write (template_content )
71+
72+ @property
73+ def date_now (self ):
74+ return datetime .datetime .now ().isoformat ()
75+
76+ @property
77+ def description (self ):
78+ description = f"""
79+ Starts { _meta .__product_name__ } at system start,
80+ use this app to automatically toggle color inversion
81+ on blinding-white windows occurs.
82+ This task generated by { _meta .__product_name__ } v{ _meta .__version__ }
83+ (author { _meta .__author__ } )
84+ """
85+ description = textwrap .dedent (description ).strip ()
86+ description = description .replace ('\n ' , ' \n ' )
87+ return description
88+
89+ @property
90+ def author (self ):
91+ return f"{ os .environ ['userdomain' ]} \\ { os .environ ['username' ]} "
92+
93+ @property
94+ def user_id (self ) -> str :
95+ security_descriptor = win32security .GetFileSecurity (
96+ "." , win32security .OWNER_SECURITY_INFORMATION
97+ )
98+ sid = security_descriptor .GetSecurityDescriptorOwner ()
99+ return win32security .ConvertSidToStringSid (sid )
100+
101+
102+ class SystemStartupHandler :
103+ _TASK_COMMAND = "schtasks {args} > nul 2> nul"
104+ _TASK_CREATE_COMMAND = _TASK_COMMAND .format (args = "/Create /XML \" {path}\" /TN \" {name}\" " )
105+ _TASK_DELETE_COMMAND = _TASK_COMMAND .format (args = "/Delete /F /TN \" {name}\" " )
106+ _TASK_QUERY_COMMAND = _TASK_COMMAND .format (args = "/Query /TN \" {name}\" " )
107+ task_file = inject .attr (StartupTaskGenerator )
108+
109+ def subscribe (self ):
110+ self .task_file .build_task ()
111+ os .system (self ._TASK_CREATE_COMMAND .format (
112+ path = self .task_file .result_path ,
113+ name = self .task_file .task_name
114+ ))
115+
116+ def unsubscribe (self ):
117+ os .system (self ._TASK_DELETE_COMMAND .format (
118+ name = self .task_file .task_name
119+ ))
120+
121+ @property
122+ def is_subscribed (self ):
123+ error_code = os .system (self ._TASK_QUERY_COMMAND .format (
124+ name = self .task_file .task_name
125+ ))
126+ return error_code == 0
127+
128+ @is_subscribed .setter
129+ def is_subscribed (self , value ):
130+ if value :
131+ self .subscribe ()
132+ else :
133+ self .unsubscribe ()
134+
31135
32- def is_admin ():
33- return ctypes .windll .shell32 .IsUserAnAdmin ()
136+ def start_with_admin_rights (show_console = False ):
137+ # https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx
138+ if has_admin_rights ():
139+ return
140+ return_code = _shell .ShellExecuteW (
141+ None , 'runas' , sys .executable , sys .argv [0 ]+ ' -m' , None ,
142+ Console .get_visibility_code (show_console )
143+ )
144+ return return_code > 32
0 commit comments