|
|
#Import parent folder to import current / other packages
|
|
|
from pyOpenRPA.Robot import UIDesktop #Lib to access RDP window
|
|
|
from . import ConnectorExceptions # Exceptions classes
|
|
|
import os #os for process run
|
|
|
import uuid #temp id for Template.rdp
|
|
|
import tempfile #Temporary location
|
|
|
import subprocess
|
|
|
from . import Clipboard # Clipboard functions get/set
|
|
|
import keyboard # Keyboard functions
|
|
|
import time
|
|
|
import random # random integers
|
|
|
from win32api import GetSystemMetrics # Get Screen rect
|
|
|
import pyautogui # for hotkey operations
|
|
|
# System variables for recovery scenario
|
|
|
gRecoveryWindowRDPRetryCountInt = 3 # Retry iteration count is RDP window is not responsible
|
|
|
gRecoveryWindowRDPRetryIntervalSecInt = 3 # Retry interval for reconnect
|
|
|
|
|
|
gRecoveryWindowRUNRetryCountInt = 3 # Retry iteration count is RUN window is not responsible
|
|
|
gRecoveryWindowRUNRetryIntervalSecInt = 3 # Retry interval for retry
|
|
|
|
|
|
gRecoveryCMDResponsibleRetryCountInt = 3 # Retry iteration count is CMD is not responsible
|
|
|
gRecoveryCMDResponsibleRetryIntervalSecInt = 3 # Retry interval for retry
|
|
|
|
|
|
gKeyboardHotkeyDelaySecFloat = 0.6 # Const for delay - important for work with RDP!!!!
|
|
|
|
|
|
#Connect to RDP session
|
|
|
"""
|
|
|
{
|
|
|
"Host": "", #Host address
|
|
|
"Port": "", #RDP Port
|
|
|
"Login": "", # Login
|
|
|
"Password": "", #Password
|
|
|
"Screen": {
|
|
|
"Resolution":"FullScreen", #"640x480" or "1680x1050" or "FullScreen". If Resolution not exists set full screen
|
|
|
"FlagUseAllMonitors": False, # True or False
|
|
|
"DepthBit":"" #"32" or "24" or "16" or "15"
|
|
|
}
|
|
|
}
|
|
|
"""
|
|
|
def Session(inRDPSessionConfiguration, inScreenSize550x350Bool = False):
|
|
|
#RDPConnector.SessionConnect(mConfiguration)
|
|
|
#RDPConnector.LoginPassSet("111.222.222.111","ww","dd")
|
|
|
(lRDPFile, lSessionHex) = SessionConfigurationCreate(inRDPSessionConfiguration)
|
|
|
#Set session hex in globalDict
|
|
|
inRDPSessionConfiguration["SessionHex"] = lSessionHex
|
|
|
#Set login/password
|
|
|
SessionLoginPasswordSet(inRDPSessionConfiguration["Host"],inRDPSessionConfiguration["Login"],inRDPSessionConfiguration["Password"])
|
|
|
#Start session
|
|
|
SessionRDPStart(lRDPFile, inScreenSize550x350Bool= inScreenSize550x350Bool)
|
|
|
#Remove temp file
|
|
|
time.sleep(4) #Delete file after some delay - one way to delete and run the RDP before because RDP is not read file in one moment
|
|
|
os.remove(lRDPFile) # delete the temp rdp
|
|
|
# Set the result
|
|
|
return inRDPSessionConfiguration
|
|
|
#Add login/ password to the windows credentials to run RDP
|
|
|
def SessionLoginPasswordSet(inHost, inLogin, inPassword):
|
|
|
#Clear old login/password if it exists
|
|
|
#os.system(f"cmdkey /delete:TERMSRV/{inHost}") #Dont need to delete because new user password will clear the previous creds
|
|
|
#Set login password for host
|
|
|
os.system(f'cmdkey /generic:TERMSRV/{inHost} /user:{inLogin} /pass:"{inPassword}"')
|
|
|
return None
|
|
|
#Create current .rdp file with settings
|
|
|
#Return (full path to file, session hex)
|
|
|
def SessionConfigurationCreate(inConfiguration):
|
|
|
#RobotRDPActive folder path
|
|
|
lFileFullPath=__file__
|
|
|
lFileFullPath = lFileFullPath.replace("/","\\")
|
|
|
lRobotRDPActiveFolderPath = "\\".join(lFileFullPath.split("\\")[:-1])
|
|
|
#Full path to Template.rdp file
|
|
|
lRDPTemplateFileFullPath = os.path.join(lRobotRDPActiveFolderPath, "Template.rdp")
|
|
|
#Open template file (.rdp encoding is USC-2 LE BOM = UTF-16 LE) http://qaru.site/questions/7156020/python-writing-a-ucs-2-little-endian-utf-16-le-file-with-bom
|
|
|
lRDPTemplateFileContent = open(lRDPTemplateFileFullPath, "r", encoding="utf-16-le").read()
|
|
|
#Prepare host:port
|
|
|
lHostPort=inConfiguration['Host']
|
|
|
if 'Port' in inConfiguration:
|
|
|
if inConfiguration['Port']:
|
|
|
lHostPort=f"{lHostPort}:{inConfiguration['Port']}"
|
|
|
# Generate parameter for .rdp "drivestoredirect:s:C:\;"
|
|
|
lDriveStoreDirectStr = ""
|
|
|
for lItem in inConfiguration['SharedDriveList']:
|
|
|
lDriveStoreDirectStr+=f"{lItem.upper()}:\\;" # Attention - all drives must be only in upper case!!!
|
|
|
#Replace {Width}, {Height}, {BitDepth}, {HostPort}, {Login}
|
|
|
lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{Width}", str(inConfiguration.get('Screen',{}).get("Width",1680)))
|
|
|
lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{Height}", str(inConfiguration.get('Screen',{}).get("Height",1050)))
|
|
|
lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{BitDepth}", inConfiguration.get('Screen',{}).get("DepthBit","32"))
|
|
|
lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{HostPort}", lHostPort)
|
|
|
lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{Login}", inConfiguration['Login'])
|
|
|
lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{SharedDriveList}", lDriveStoreDirectStr)
|
|
|
#Save template to temp file
|
|
|
lRDPCurrentFileFullPath = os.path.join(tempfile.gettempdir(), f"{uuid.uuid4().hex}.rdp")
|
|
|
open(lRDPCurrentFileFullPath, "w", encoding="utf-16-le").write(lRDPTemplateFileContent)
|
|
|
#Return .rdp full path
|
|
|
return (lRDPCurrentFileFullPath, (lRDPCurrentFileFullPath.split("\\")[-1])[0:-4])
|
|
|
#RDPSessionStart
|
|
|
def SessionRDPStart(inRDPFilePath, inScreenSize550x350Bool = False):
|
|
|
#Disable certificate warning
|
|
|
lCMDString = 'reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client" /v "AuthenticationLevelOverride" /t "REG_DWORD" /d 0 /f'
|
|
|
os.system(lCMDString)
|
|
|
#run rdp session
|
|
|
lItemArgs = [inRDPFilePath]
|
|
|
subprocess.Popen(lItemArgs, shell=True)
|
|
|
#Wait for UAC unknown publisher exists
|
|
|
lRDPFileName = (inRDPFilePath.split("\\")[-1])[0:-4]
|
|
|
lWaitResult = UIDesktop.UIOSelectorsSecs_WaitAppear_List(
|
|
|
[
|
|
|
[{"title": "Подключение к удаленному рабочему столу", "class_name": "#32770", "backend": "win32"},
|
|
|
{"title": "Боль&ше не выводить запрос о подключениях к этому компьютеру", "friendly_class_name": "CheckBox"}],
|
|
|
[{"title": "Remote Desktop Connection", "class_name": "#32770", "backend": "win32"},
|
|
|
{"title": "D&on't ask me again for connections to this computer",
|
|
|
"friendly_class_name": "CheckBox"}],
|
|
|
[{"title_re": f"{lRDPFileName}.*",
|
|
|
"class_name": "TscShellContainerClass", "backend": "win32"},{"depth_start":3, "depth_end": 3, "class_name":"UIMainClass"}]
|
|
|
],
|
|
|
30
|
|
|
)
|
|
|
#Enable certificate warning
|
|
|
lCMDString = 'reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client" /v "AuthenticationLevelOverride" /t "REG_DWORD" /d 2 /f'
|
|
|
os.system(lCMDString)
|
|
|
#Click if 0 is appear (RUS)
|
|
|
if 0 in lWaitResult:
|
|
|
#Check the box do not retry
|
|
|
UIDesktop.UIOSelector_Get_UIO([{"title": "Подключение к удаленному рабочему столу", "backend": "win32"},
|
|
|
{"title": "Боль&ше не выводить запрос о подключениях к этому компьютеру", "friendly_class_name": "CheckBox"}]).check()
|
|
|
#Go to connection
|
|
|
UIDesktop.UIOSelector_Get_UIO([{"title": "Подключение к удаленному рабочему столу", "backend": "win32"},
|
|
|
{"title":"Подкл&ючить", "class_name":"Button"}]).click()
|
|
|
lWaitResult = UIDesktop.UIOSelectorsSecs_WaitAppear_List(
|
|
|
[
|
|
|
[{"title_re": f"{lRDPFileName}.*",
|
|
|
"class_name": "TscShellContainerClass", "backend": "win32"}]
|
|
|
],
|
|
|
30
|
|
|
)
|
|
|
# Click if 1 is appear (ENG)
|
|
|
if 1 in lWaitResult:
|
|
|
# Check the box do not retry
|
|
|
UIDesktop.UIOSelector_Get_UIO([{"title": "Remote Desktop Connection", "class_name": "#32770", "backend": "win32"},
|
|
|
{"title": "D&on't ask me again for connections to this computer",
|
|
|
"friendly_class_name": "CheckBox"}]).check()
|
|
|
# Go to connection
|
|
|
UIDesktop.UIOSelector_Get_UIO([{"title": "Remote Desktop Connection", "class_name": "#32770", "backend": "win32"},
|
|
|
{"title": "Co&nnect", "class_name": "Button"}]).click()
|
|
|
lWaitResult = UIDesktop.UIOSelectorsSecs_WaitAppear_List(
|
|
|
[
|
|
|
[{"title_re": f"{lRDPFileName}.*",
|
|
|
"class_name": "TscShellContainerClass", "backend": "win32"}]
|
|
|
],
|
|
|
30
|
|
|
)
|
|
|
# Raise exception if RDP is not active
|
|
|
if len(lWaitResult) == 0:
|
|
|
raise ConnectorExceptions.SessionWindowNotExistError("Error when initialize the RDP session - No RDP windows has appreared!")
|
|
|
time.sleep(3) # Wait for init
|
|
|
if inScreenSize550x350Bool: SessionScreenSize_X_Y_W_H(inSessionHex = lRDPFileName, inXInt = 10, inYInt = 10, inWInt = 550, inHInt = 350) #Prepare little window
|
|
|
return None
|
|
|
|
|
|
#Set fullscreen for app
|
|
|
def SessionScreenFull(inSessionHex, inLogger = None, inRDPConfigurationItem = None):
|
|
|
########################################
|
|
|
lWindowRDPRetryIterator = 0 # Retry iterator if RDP window is not active
|
|
|
lRDPConfigurationItem = inRDPConfigurationItem # Get the RDP configuration item
|
|
|
lL = inLogger # Get the logger instance
|
|
|
lRDPWindow = None # Init the variable
|
|
|
while lWindowRDPRetryIterator<gRecoveryWindowRDPRetryCountInt: # Loop iteration to connect to RDP
|
|
|
try: # Try to get RDP window
|
|
|
lRDPWindow = UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHex}.*", "backend": "win32"}])
|
|
|
lWindowRDPRetryIterator = gRecoveryWindowRDPRetryCountInt # Set last iterator to turn off the loop
|
|
|
# RDP window has been detected - go to set focus and maximize
|
|
|
lRDPWindow.set_focus()
|
|
|
lRDPWindow.maximize()
|
|
|
if not SessionIsFullScreen(inSessionHex):
|
|
|
lRDPWindow.type_keys("^%{BREAK}")
|
|
|
time.sleep(0.5)
|
|
|
except Exception as e: # RDP window is not exist - try to reconnect
|
|
|
if lL: lL.warning(f"RDP::SessionScreenFull: RDP window is not exist - try sleep {gRecoveryWindowRDPRetryIntervalSecInt}[s.] and then to reconnect. SessionHex: {inSessionHex}. Current retry iterator is {lWindowRDPRetryIterator}")
|
|
|
if lRDPConfigurationItem:
|
|
|
time.sleep(gRecoveryWindowRDPRetryIntervalSecInt) # Before try to reconnect sleep
|
|
|
# Try to reconnect the RDP
|
|
|
SessionClose(inSessionHexStr=inSessionHex) # Close the
|
|
|
Session(lRDPConfigurationItem) # Create RDP session
|
|
|
inSessionHex = lRDPConfigurationItem["SessionHex"] # Get new session hex after reconnect
|
|
|
SystemRDPWarningClickOk() # Click all warning messages
|
|
|
lWindowRDPRetryIterator = lWindowRDPRetryIterator + 1 # increase the iterator
|
|
|
if lWindowRDPRetryIterator >= gRecoveryWindowRDPRetryCountInt: # Raise the error if retry count is over
|
|
|
if lL: lL.warning(f"RDP::SessionScreenFull: Retry count is over. Raise the error. SessionHex: {inSessionHex}.") # Log the info
|
|
|
raise ConnectorExceptions.SessionWindowNotExistError() # raise the error
|
|
|
else:
|
|
|
if lL: lL.warning(f"RDP::SessionScreenFull: Has no RDP configuration item - don't reconnect. Raise the error. SessionHex: {inSessionHex}") # Log the info
|
|
|
raise ConnectorExceptions.SessionWindowNotExistError() # raise the error
|
|
|
return None
|
|
|
|
|
|
# Set the screen size
|
|
|
def SessionScreenSize_X_Y_W_H(inSessionHex, inXInt, inYInt, inWInt, inHInt):
|
|
|
lDoBool = True
|
|
|
while lDoBool:
|
|
|
#Prepare little window
|
|
|
try:
|
|
|
lRDPWindow = UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHex}.*", "backend": "win32"}])
|
|
|
except Exception as e:
|
|
|
return None
|
|
|
try:
|
|
|
lRDPWindow.set_focus()
|
|
|
if SessionIsFullScreen(inSessionHex):
|
|
|
lRDPWindow.type_keys("^%{BREAK}")
|
|
|
time.sleep(0.5)
|
|
|
lRDPWindow.restore()
|
|
|
time.sleep(0.5)
|
|
|
lRDPWindow.move_window(inXInt,inYInt,inWInt,inHInt)
|
|
|
except Exception as e:
|
|
|
time.sleep(1)
|
|
|
else:
|
|
|
lDoBool = False
|
|
|
return None
|
|
|
|
|
|
# Set Little window of the session
|
|
|
def SessionScreen100x550(inSessionHex):
|
|
|
SessionScreenSize_X_Y_W_H(inSessionHex = inSessionHex, inXInt = 10, inYInt = 10, inWInt = 550, inHInt = 100)
|
|
|
return None
|
|
|
# Session - close window
|
|
|
def SessionClose(inSessionHexStr):
|
|
|
#Close window
|
|
|
try:
|
|
|
UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHexStr}.*", "backend": "win32"}]).close()
|
|
|
except Exception as e:
|
|
|
pass
|
|
|
#Type command in CMD
|
|
|
# inSessionHex - SessionHex to catch window
|
|
|
# inModeStr "LISTEN", "CROSSCHECK", "RUN"
|
|
|
# "LISTEN" - Get result of the cmd command in result TODO get home script
|
|
|
# "CROSSCHECK" - Check if the command was successufully sent TODO get home script
|
|
|
# "RUN" - Run without crosscheck and get clipboard
|
|
|
# return {
|
|
|
# "OutStr": <> # Result string
|
|
|
# "IsResponsibleBool": True|False # Flag is RDP is responsible - works only when inModeStr = CROSSCHECK
|
|
|
# }
|
|
|
# example Connector.SessionCMDRun("4d1e48f3ff6c45cc810ea25d8adbeb50","start notepad", "RUN")
|
|
|
def SessionCMDRun(inSessionHex,inCMDCommandStr = "echo 1", inModeStr="CROSSCHECK", inClipboardTimeoutSec = 5, inLogger=None, inRDPConfigurationItem=None):
|
|
|
lL = inLogger # Init the logger
|
|
|
# Init the result dict
|
|
|
lResult = {"OutStr": None,"IsResponsibleBool":True}
|
|
|
SessionScreenFull(inSessionHex, inLogger=lL, inRDPConfigurationItem=inRDPConfigurationItem) # Enter full screen mode with recovery scenario
|
|
|
time.sleep(2)
|
|
|
# Run CMD operations
|
|
|
lResult = SystemCMDRun(inSessionHexStr = inRDPConfigurationItem["SessionHex"], inCMDCommandStr = inCMDCommandStr, inModeStr = inModeStr, inClipboardTimeoutSec = inClipboardTimeoutSec, inLogger=lL)
|
|
|
# Exit fullscreen mode
|
|
|
SessionScreenSize_X_Y_W_H(inSessionHex=inSessionHex, inXInt=10, inYInt=10, inWInt=550,
|
|
|
inHInt=350) # Prepare little window
|
|
|
# Check if session is in Full screen mode
|
|
|
# Return True - is in fullscreen
|
|
|
# example print(Connector.SessionIsFullScreen(""))
|
|
|
def SessionIsFullScreen(inSessionHexStr):
|
|
|
#Default resul
|
|
|
lResult = False
|
|
|
lWeight = GetSystemMetrics(0)
|
|
|
lHeight = GetSystemMetrics(1)
|
|
|
#Get window screen
|
|
|
try:
|
|
|
lRectangle = UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHexStr}.*", "backend": "win32"}]).rectangle()
|
|
|
except Exception as e:
|
|
|
return lResult
|
|
|
# Get Height/Weight
|
|
|
lSessionWeight = lRectangle.right - lRectangle.left
|
|
|
lSessionHeight = lRectangle.bottom - lRectangle.top
|
|
|
#Case fullscreen
|
|
|
if lSessionHeight == lHeight and lSessionWeight == lWeight:
|
|
|
lResult = True
|
|
|
return lResult
|
|
|
|
|
|
# Check if session is in minimized screen mode
|
|
|
# Return True - is in minimized
|
|
|
# example print(Connector.SessionIsFullScreen(""))
|
|
|
def SessionIsMinimizedScreen(inSessionHexStr):
|
|
|
#Default result
|
|
|
lResult = False
|
|
|
#Get window screen
|
|
|
try:
|
|
|
lResult = UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHexStr}.*", "backend": "win32"}]).is_minimized()
|
|
|
except Exception as e:
|
|
|
pass
|
|
|
return lResult
|
|
|
# Check if RDP session is responsible (check with random combination in cmd)
|
|
|
# Attention - function will be work fine if RDP will be in full screen mode!!! (see def SessionScreenFull)
|
|
|
# Return True - is responsible; False - is not responsible
|
|
|
#Type command in CMD
|
|
|
# inFlagDoCrossCheck: True - Do check that CMD is executed (the text response will not be available)
|
|
|
# inModeStr "LISTEN", "CROSSCHECK", "RUN"
|
|
|
# "LISTEN" - Get result of the cmd command in result TODO get home script
|
|
|
# "CROSSCHECK" - Check if the command was successufully sent TODO get home script
|
|
|
# "RUN" - Run without crosscheck and get clipboard
|
|
|
# inClipboardTimeoutSec # Second for wait when clipboard will changed
|
|
|
# return {
|
|
|
# "OutStr": <> # Result string
|
|
|
# "IsResponsibleBool": True|False # Flag is RDP is responsible - works only when inModeStr = CROSSCHECK
|
|
|
# }
|
|
|
# example Connector.SessionCMDRun("4d1e48f3ff6c45cc810ea25d8adbeb50","start notepad", "RUN")
|
|
|
def SystemCMDRun(inSessionHexStr, inCMDCommandStr = "echo 1", inModeStr="CROSSCHECK", inClipboardTimeoutSec = 5, inLogger = None):
|
|
|
lRDPWindow = None # Init the UI object
|
|
|
try:
|
|
|
lRDPWindow = UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHexStr}.*", "backend": "win32"}])
|
|
|
except Exception as e:
|
|
|
raise ConnectorExceptions.SessionWindowNotExistError() # Raise error of gui window
|
|
|
lL = inLogger # Alias for logger
|
|
|
lResult = {"OutStr": None,"IsResponsibleBool":True} # Init the result dict
|
|
|
lClipboardTextOld = str(random.randrange(999,9999999)) # Set random text to clipboard (for check purposes that clipboard text has been changed)
|
|
|
Clipboard.TextSet(lClipboardTextOld)
|
|
|
lCrosscheckKeyStr = str(random.randrange(999,9999999))
|
|
|
lRecoveryCMDResponsibleRetryIteratorInt = 0 # Init the retry iterator
|
|
|
while lRecoveryCMDResponsibleRetryIteratorInt<gRecoveryCMDResponsibleRetryCountInt: # loop for retry
|
|
|
# # # # # # # # # # # # # OPEN WINDOW RUN # # # # # # # # # # # # # # #
|
|
|
lRecoveryWindowRUNRetryIteratorInt = 0 # Init the retry iterator
|
|
|
while lRecoveryWindowRUNRetryIteratorInt<gRecoveryWindowRUNRetryCountInt: # loop for retry
|
|
|
lCMDPostFixStr = "" # Case default "RUN"
|
|
|
if inModeStr == "CROSSCHECK":
|
|
|
lCMDPostFixStr = f"& (echo {lCrosscheckKeyStr} | clip)"
|
|
|
elif inModeStr == "LISTEN":
|
|
|
lCMDPostFixStr = f"| clip"
|
|
|
KeyboardHotkey("win","r") # win+r
|
|
|
KeyboardHotkey("ctrl","a") # Select all
|
|
|
keyboard.send("backspace") # Delete selected all
|
|
|
time.sleep(gKeyboardHotkeyDelaySecFloat) # Wait for RUN window will appear ctrl+a+backspace is async - so we need some timeout...
|
|
|
lInputStr = f"cmd /c ({inCMDCommandStr}) {lCMDPostFixStr}" # Generate the output string for RUN window
|
|
|
keyboard.write(lInputStr) # Write new text
|
|
|
time.sleep(gKeyboardHotkeyDelaySecFloat)
|
|
|
KeyboardHotkey("ctrl","a") # Select all
|
|
|
KeyboardHotkey("ctrl","c") # Copy data
|
|
|
# Check the clipboard
|
|
|
lClipboardWaitTimeStartSec = time.time()
|
|
|
lClipboardStr = Clipboard.TextGet() # Get text from clipboard
|
|
|
while lClipboardStr != lInputStr and (time.time() - lClipboardWaitTimeStartSec) <= inClipboardTimeoutSec:
|
|
|
lClipboardStr = Clipboard.TextGet() # Get text from clipboard
|
|
|
time.sleep(0.5) # wait some time for the next operation
|
|
|
if lClipboardStr == lInputStr: # Cross check the clipboard data and input string
|
|
|
lRecoveryWindowRUNRetryIteratorInt = gRecoveryWindowRUNRetryCountInt # Set final count to block the loop
|
|
|
else: # Failed - wait and retry
|
|
|
if lL: lL.warning(f"RDP::SystemCMDRun: Window run doesn't appear. Wait for {gRecoveryWindowRUNRetryIntervalSecInt}[s.] and retry. Current retry iterator is {lRecoveryWindowRUNRetryIteratorInt}. CMD Str: {lInputStr}") # Log the error
|
|
|
lRecoveryWindowRUNRetryIteratorInt = lRecoveryWindowRUNRetryIteratorInt + 1 # Increment the iterator
|
|
|
if lRecoveryWindowRUNRetryIteratorInt == gRecoveryWindowRUNRetryCountInt:
|
|
|
if lL: lL.warning(f"RDP::SystemCMDRun: Retry count is over. Raise the error. CMD Str: {lInputStr}") # Log the error
|
|
|
raise ConnectorExceptions.RUNExistError() # Raise the error
|
|
|
time.sleep(gRecoveryWindowRUNRetryIntervalSecInt) # wait for some seconds before new iteration
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
# # # # # # # # # # # # # RUN CMD # # # # # # # # # # # # # # #
|
|
|
keyboard.press_and_release('enter') # Execute CMD
|
|
|
if inModeStr == "CROSSCHECK" or inModeStr == "LISTEN": # Get OutStr (Case CROSSCHECK and LISTEN)
|
|
|
lClipboardWaitTimeStartSec = time.time()
|
|
|
lResult["OutStr"] = Clipboard.TextGet() # Get text from clipboard
|
|
|
while lResult["OutStr"] == lClipboardTextOld and (time.time() - lClipboardWaitTimeStartSec) <= inClipboardTimeoutSec:
|
|
|
lResult["OutStr"] = Clipboard.TextGet() # Get text from clipboard
|
|
|
time.sleep(0.5) # wait some time for the next operation
|
|
|
if lResult["OutStr"] == lClipboardTextOld: # If value hasn't been changed - retry send
|
|
|
lRecoveryCMDResponsibleRetryIteratorInt = lRecoveryCMDResponsibleRetryIteratorInt + 1 # increment the iterator
|
|
|
if lL: lL.warning(f"RDP::SystemCMDRun: CMD command doesn't been executed (no changes in clipboard data). Wait for {gRecoveryCMDResponsibleRetryIntervalSecInt}[s.] and retry from start window RUN. Current retry iterator is {lRecoveryCMDResponsibleRetryIteratorInt}. CMD Str: {lInputStr}, Clipboard data: {lResult['OutStr']}") # Log the error
|
|
|
if lRecoveryCMDResponsibleRetryIteratorInt >= gRecoveryCMDResponsibleRetryCountInt: # raise the error if retry count is exceeded
|
|
|
if lL: lL.warning(f"RDP::SystemCMDRun: Retry count is over. Raise the error.") # Log the error
|
|
|
raise ConnectorExceptions.CMDResponsibleError() # Raise the error
|
|
|
time.sleep(gRecoveryCMDResponsibleRetryIntervalSecInt) # wait for some seconds before new iteration
|
|
|
else: # Data was recieved - do crosscheck
|
|
|
if inModeStr == "CROSSCHECK":
|
|
|
if lResult["OutStr"] == f"{lCrosscheckKeyStr} \r\n\x00\x00\x00\x00\x00": # it is ok - do futher check
|
|
|
lResult["IsResponsibleBool"] = True
|
|
|
lRecoveryCMDResponsibleRetryIteratorInt = gRecoveryCMDResponsibleRetryCountInt # turn off the iterator
|
|
|
else:
|
|
|
lResult["IsResponsibleBool"] = False
|
|
|
lRecoveryCMDResponsibleRetryIteratorInt = lRecoveryCMDResponsibleRetryIteratorInt + 1 # increment the iterator
|
|
|
if lL: lL.warning(f"RDP::SystemCMDRun: CMD command doesn't been executed (wrong clipboard data). Wait for {gRecoveryCMDResponsibleRetryIntervalSecInt}[s.] and retry from start window RUN. Current retry iterator is {lRecoveryCMDResponsibleRetryIteratorInt}. CMD Str: {lInputStr}, Clipboard data: {lResult['OutStr']}") # Log the error
|
|
|
if lRecoveryCMDResponsibleRetryIteratorInt >= gRecoveryCMDResponsibleRetryCountInt: # raise the error if retry count is exceeded
|
|
|
if lL: lL.warning(f"RDP::SystemCMDRun: Retry count is over. Raise the error.") # Log the error
|
|
|
raise ConnectorExceptions.CMDResponsibleError() # Raise the error
|
|
|
time.sleep(gRecoveryCMDResponsibleRetryIntervalSecInt) # wait for some seconds before new iteration
|
|
|
else: # clipboard data has been changed but mode is not crosscheck - return success from function
|
|
|
lRecoveryCMDResponsibleRetryIteratorInt = gRecoveryCMDResponsibleRetryCountInt # turn off the iterator
|
|
|
else: # Success - no cross validation is aaplicable
|
|
|
lRecoveryCMDResponsibleRetryIteratorInt = gRecoveryCMDResponsibleRetryCountInt # turn off the iterator
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
return lResult # return the result
|
|
|
# Check if current RDP is responsible
|
|
|
def SystemRDPIsResponsible(inSessionHexStr):
|
|
|
return SystemCMDRun(inSessionHexStr = inSessionHexStr, inCMDCommandStr = "echo 1", inModeStr="CROSSCHECK")["IsResponsibleBool"]
|
|
|
# Click OK on error messages
|
|
|
def SystemRDPWarningClickOk():
|
|
|
# Try to click OK Error window in RUS version
|
|
|
while UIDesktop.UIOSelector_Exist_Bool([{"title": "Подключение к удаленному рабочему столу", "class_name": "#32770", "backend": "win32"},
|
|
|
{"title": "ОК", "class_name": "Button"}]):
|
|
|
try:
|
|
|
UIDesktop.UIOSelector_Get_UIO([{"title": "Подключение к удаленному рабочему столу", "class_name": "#32770", "backend": "win32"},
|
|
|
{"title": "ОК", "class_name": "Button"}]).click()
|
|
|
except Exception as e:
|
|
|
pass
|
|
|
# Try to click OK Error window in ENG version
|
|
|
while UIDesktop.UIOSelector_Exist_Bool([{"title": "Remote Desktop Connection", "class_name": "#32770", "backend": "win32"},
|
|
|
{"title": "OK", "class_name": "Button"}]):
|
|
|
try:
|
|
|
UIDesktop.UIOSelector_Get_UIO([{"title": "Remote Desktop Connection", "class_name": "#32770", "backend": "win32"},
|
|
|
{"title": "OK", "class_name": "Button"}]).click()
|
|
|
except Exception as e:
|
|
|
pass
|
|
|
# Bugfix with RDP work
|
|
|
def KeyboardHotkey(inModifierKeyStr,inKeyStr, inDelaySecFloat=gKeyboardHotkeyDelaySecFloat):
|
|
|
keyboard.press(inModifierKeyStr)
|
|
|
time.sleep(inDelaySecFloat)
|
|
|
keyboard.send(inKeyStr)
|
|
|
time.sleep(inDelaySecFloat)
|
|
|
keyboard.release(inModifierKeyStr)
|
|
|
time.sleep(inDelaySecFloat) |