1111import os
1212import time
1313from urllib .parse import urlparse
14+ import win32security
15+ import sys
1416
1517
1618class DgpSession :
@@ -211,6 +213,12 @@ class ErrorManager:
211213 url = "https://github.com/fa0311/DMMGamePlayerFastLauncher/issues" ,
212214 )
213215
216+ elevate_admin_error : ErrorManagerType = ErrorManagerType (
217+ message = "Failed to elevate to administrator privileges." ,
218+ solution = "Report an Issues." ,
219+ url = "https://github.com/fa0311/DMMGamePlayerFastLauncher/issues" ,
220+ )
221+
214222 def error (self , error : ErrorManagerType , log : str | None = None ):
215223 output = filter (
216224 lambda x : x != None ,
@@ -225,7 +233,86 @@ def info(self, text: str):
225233 print (text )
226234
227235
236+ class ProcessManager :
237+ non_request_admin : bool = False
238+ non_bypass_uac : bool = False
239+ error_manager : ErrorManager
240+
241+ def __init__ (self , error_manager : ErrorManager ) -> None :
242+ self .error_manager = error_manager
243+
244+ def run (
245+ self , args : dict [str ], admin : bool = False , force : bool = False
246+ ) -> subprocess .Popen [bytes ] | None :
247+ print (" " .join (args ))
248+ if admin :
249+ if not self .non_bypass_uac and not force :
250+ run_bypass_uac ()
251+ elif self .non_request_admin :
252+ self .error_manager .error (error = ErrorManager .permission_error )
253+ else :
254+ args = [f'"{ arg } "' for arg in args ]
255+ ctypes .windll .shell32 .ShellExecuteW (
256+ None , "runas" , args [0 ], " " .join (args [1 :]), None , 1
257+ )
258+ else :
259+ return subprocess .Popen (
260+ args , shell = True , stdout = subprocess .PIPE , stderr = subprocess .PIPE
261+ )
262+
263+
264+ def get_sid () -> str :
265+ desc = win32security .GetFileSecurity ("." , win32security .OWNER_SECURITY_INFORMATION )
266+ sid = desc .GetSecurityDescriptorOwner ()
267+ sidstr = win32security .ConvertSidToStringSid (sid )
268+ return sidstr
269+
270+
271+ def run_bypass_uac ():
272+ schtasks_file = "schtasks_v1_" + os .getlogin ()
273+ schtasks_name = f"\Microsoft\Windows\DMMGamePlayerFastLauncher\{ schtasks_file } "
274+
275+ run_args = [arg .schtasks_path , "/run" , "/tn" , schtasks_name ]
276+
277+ if process_manager .run (run_args ).wait () == 1 :
278+ schtasks_xml_path = (
279+ r"{appdata}\DMMGamePlayerFastLauncher\assets\{name}.xml" .format (
280+ appdata = os .environ ["APPDATA" ], name = schtasks_file
281+ )
282+ )
283+ schtasks_task_path = (
284+ r"{appdata}\DMMGamePlayerFastLauncher\Tools\Task.exe" .format (
285+ appdata = os .environ ["APPDATA" ]
286+ )
287+ )
288+ with open ("assets/template.xml" , "r" ) as f :
289+ template = f .read ()
290+
291+ with open (f"assets/{ schtasks_file } .xml" , "w" ) as f :
292+ f .write (
293+ template .replace (r"{{UID}}" , schtasks_file )
294+ .replace (r"{{SID}}" , get_sid ())
295+ .replace (r"{{COMMAND}}" , schtasks_task_path )
296+ .replace (r"{{WORKING_DIRECTORY}}" , os .getcwd ())
297+ )
298+
299+ create_args = [
300+ arg .schtasks_path ,
301+ "/create" ,
302+ "/xml" ,
303+ schtasks_xml_path ,
304+ "/tn" ,
305+ schtasks_name ,
306+ ]
307+ process_manager .run (create_args , admin = True , force = True )
308+ time .sleep (3 )
309+ process_manager .run (run_args ).wait ()
310+ time .sleep (5 )
311+ sys .exit ()
312+
313+
228314error_manager = ErrorManager ()
315+ process_manager = ProcessManager (error_manager )
229316
230317argpar = argparse .ArgumentParser (
231318 prog = "DMMGamePlayerFastLauncher" ,
@@ -239,9 +326,14 @@ def info(self, text: str):
239326argpar .add_argument ("--skip-exception" , action = "store_true" )
240327argpar .add_argument ("--https-proxy-uri" , default = None )
241328argpar .add_argument ("--non-request-admin" , action = "store_true" )
329+ argpar .add_argument ("--non-bypass-uac" , action = "store_true" )
330+ argpar .add_argument ("--schtasks-path" , default = "schtasks.exe" )
331+
242332try :
243333 arg = argpar .parse_args ()
244334 error_manager .skip = arg .skip_exception
335+ process_manager .non_request_admin = arg .non_request_admin
336+ process_manager .non_bypass_uac = arg .non_bypass_uac
245337except :
246338 error_manager .error (error = ErrorManager .argument_error )
247339
@@ -324,20 +416,17 @@ def info(self, text: str):
324416 dmm_args = dmm_args + arg .game_args .split (" " )
325417 print (game_path )
326418 start_time = time .time ()
327- process = subprocess .Popen (
328- [game_path ] + dmm_args , shell = True , stdout = subprocess .PIPE
329- )
419+ process = process_manager .run ([game_path ] + dmm_args )
330420 for line in process .stdout :
331421 text = line .decode ("utf-8" ).strip ()
332422 print (text )
333- if time .time () - start_time < 2 and not arg . skip_exception :
423+ if time .time () - start_time < 2 :
334424 if response ["data" ]["is_administrator" ]:
335- if not ctypes .windll .shell32 .IsUserAnAdmin () and not arg .non_request_admin :
336- ctypes .windll .shell32 .ShellExecuteW (
337- None , "runas" , game_path , response ["data" ]["execute_args" ], None , 1
338- )
339- else :
340- error_manager .error (error = ErrorManager .permission_error )
425+ process = process_manager .run ([game_path ] + dmm_args , admin = True )
426+ else :
427+ error_manager .error (
428+ error = ErrorManager .startup_error , log = json .dumps (response )
429+ )
341430
342431elif response ["result_code" ] == 307 :
343432 error_manager .error (error = ErrorManager .auth_device_error )
0 commit comments