BigDaddyO Posted January 2, 2020 Share Posted January 2, 2020 This allows you to easily redirect your existing RDP session to the console so it will maintain an active Desktop. This code has a UI to perform the redirect, but to actually use it with your automation, you would need to incorporate it into your code as once it's run it will kick you out of the RDP session since it was redirected to the console. When I was using this, I had another automation running in the background so I ran this to redirect to console and my other automation script ran after hours when it was schedule. expandcollapse popup#Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Outfile_type=a3x #AutoIt3Wrapper_Outfile=RedirectToConsole.a3x #AutoIt3Wrapper_UseX64=N #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #cs Make a remote connection not loose access to the desktop after disconnecting. https://support.smartbear.com/testcomplete/docs/testing-with/running/via-rdp/keeping-computer-unlocked.html https://support.microsoft.com/en-us/help/302801/the-use-of-tscon-exe-can-leave-a-previously-locked-console-unlocked for /f "skip=1 tokens=3" %%s in ('query user %USERNAME%') do (%windir%\System32\tscon.exe %%s /dest:console) "C:\<path to dc64.exe>\dc64.exe" -width=1920 -height=1200 -depth=32 for /f "skip=1 tokens=3" %%s in ('query user %USERNAME%') do ( %windir%\System32\tscon.exe %%s /dest:console ) Must be run under x64 not 32, use this to run the execute under 64bit _WinAPI_Wow64EnableWow64FsRedirection(True) ShellExecute(@WindowsDir & '\System32\tscon.exe', '1 /dest:console', '', 'runas') _WinAPI_Wow64EnableWow64FsRedirection(False) Once we have been redirected to console, ensure we are using the largest display available can't tell what the available displays are until we have gone into the console at least once... Create an .ini for use on that computer after to load the dropdown? Default to the last one used, Largest if nothing else was used yet. This doesn't work within Citrix as its a totally different connection type Create a log file to track who initiated the redirect and where they connected in from #ce #include <WinAPIFiles.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <Array.au3> #include <ColorConstants.au3> #include <GuiComboBox.au3> Opt("GUIOnEventMode", 1) ;Use Event mode to call functions directly $sLogFile = @ScriptDir & "\redirect.log" $sSeperator = "---------------------------------------------------------------------------------------------" ;===================================================================================== ;=== Used to figure out what SessionID we are logged in with and if its a remote connection ;===================================================================================== ;Options for the $SessionID Global Static $WTS_CURRENT_SESSION = -1 ;Options for the $WTSInfoClass Global Static $WTSInitialProgram = 0 Global Static $WTSApplicationName = 1 Global Static $WTSWorkingDirectory = 2 Global Static $WTSOEMId = 3 Global Static $WTSSessionId = 4 Global Static $WTSUserName = 5 Global Static $WTSWinStationName = 6 Global Static $WTSDomainName = 7 Global Static $WTSConnectState = 8 Global Static $WTSClientBuildNumber = 9 Global Static $WTSClientName = 10 Global Static $WTSClientDirectory = 11 Global Static $WTSClientProductId = 12 Global Static $WTSClientHardwareId = 13 Global Static $WTSClientAddress = 14 Global Static $WTSClientDisplay = 15 Global Static $WTSClientProtocolType = 16 Global Static $WTSIdleTime = 17 Global Static $WTSLogonTime = 18 Global Static $WTSIncomingBytes = 19 Global Static $WTSOutgoingBytes = 20 Global Static $WTSIncomingFrames = 21 Global Static $WTSOutgoingFrames = 22 Global Static $WTSClientInfo = 23 Global Static $WTSSessionInfo = 24 Global Static $WTSSessionInfoEx = 25 Global Static $WTSConfigInfo = 26 Global Static $WTSValidationInfo = 27 Global Static $WTSSessionAddressV4 = 28 Global Static $WTSIsRemoteSession = 29 Global $sINIfile = @ScriptDir & "\redirect.ini" $iGuiW = 500 $iGuiH = 200 $hGUI = GUICreate("RDP Desktop Redirect", $iGuiW, $iGuiH) GUISetOnEvent($GUI_EVENT_CLOSE, "_ExitApp") GUICtrlCreateLabel("This will redirect your current session to Console." & @CRLF & "This desktop will remain active, but you will be disconnected.", 0, 13, $iGuiW, 40, $SS_CENTER) GUICtrlSetFont(-1, 10, 600) GUICtrlCreateLabel("Select the desktop Display Settings to use after the redirect", 0, 70, $iGuiW, 17, $SS_CENTER) $hDisplaySetting = GUICtrlCreateCombo("<Max>", 40, 90, 240, 20) ;This will be loaded from .ini if found $hBtnRedirect = GUICtrlCreateButton("Redirect and Disconnect", 40, $iGuiH - 50, 160, 25) GUICtrlSetOnEvent($hBtnRedirect, "_RedirectNow") GUICtrlCreateButton("Exit", $iGuiW - 140, $iGuiH - 50, 100, 25) GUICtrlSetOnEvent(-1, "_ExitApp") ;Disable the Redirect button if this is not an RDP session ; Create a red note next to redirect to say Not an RDP session. $sConnType = _WTSQuerySessionInformation($WTS_CURRENT_SESSION, $WTSClientProtocolType) ;What type of connection was used for this session 2 = RDP ConsoleWrite("$WTSClientProtocolType " & $sConnType & @CRLF) If $sConnType <> 2 Then ;2 = RDP connection GUICtrlCreateLabel("Not an RDP connection", 205, $iGuiH - 45, 140, 17) GUICtrlSetColor(-1, $COLOR_RED) GUICtrlSetState($hBtnRedirect, $GUI_DISABLE) EndIf ;Read the .ini if exists and populate the combobox $aDisplayOptions = IniReadSection($sINIfile, "Display") If @error Then GUICtrlCreateLabel("List available after the first redirect", 285, 93, $iGuiW - 285, 17) Else ;load the returned array into the combobox For $i = 1 to $aDisplayOptions[0][0] _GUICtrlComboBox_AddString($hDisplaySetting, $aDisplayOptions[$i][1]) Next EndIf GUISetState(@SW_SHOW, $hGUI) While 1 sleep(100) WEnd Func _RedirectNow() FileWriteLine($sLogFile, $sSeperator & @CRLF & " " & @MON & "/" & @MDAY & "/" & @YEAR & " " & @HOUR & ":" & @MIN & ":" & @SEC & "." & @MSEC) $id = _WTSQuerySessionInformation($WTS_CURRENT_SESSION, $WTSSessionId) ;Get the current users SessionId ConsoleWrite("My Session ID: " & $id & @CRLF) $Username = _WTSQuerySessionInformation($WTS_CURRENT_SESSION, $WTSUserName) ;Get the username for the specified session ConsoleWrite("My Username: " & $Username & @CRLF) $sConnFrom = _WTSQuerySessionInformation($WTS_CURRENT_SESSION, $WTSClientName) ;Where the connected user is from (computername) ConsoleWrite("Connected From " & $sConnFrom & @CRLF) If $sConnType = 2 Then ; 2 = RDP Local $aRes[4] ;Store the resolution details needed to update res after switching to console $sDisplaySelection = GUICtrlRead($hDisplaySetting) ;what did the user pick from the dropdown? ;ConsoleWrite("About to redirect to Console: " & @HOUR & ":" & @MIN & ":" & @SEC & "." & @MSEC & @CRLF) FileWriteLine($sLogFile, " RDP Session " & $id & " Redirect to console session 0 initiated by " & $Username & " from " & $sConnFrom) _RedirectToConsole($id) ;Redirect the logged in RDP session to the console to maintain an active desktop for automation While _WTSQuerySessionInformation($WTS_CURRENT_SESSION, $WTSClientProtocolType) = 2 ;Sleep until the Session type is no longer RDP sleep(200) WEnd Sleep(2000) ;Pull every available graphic option from the console and save to the .ini file for selection on the next run IniDelete($sINIfile, "Display", "") ;Delete any existing display settings $aAllOptions = _EnumDisplaySettingsEx() ;Retrieve array of all valid display settings For $i = 0 to UBound($aAllOptions) - 1 IniWrite($sINIfile, "Display", $i, $aAllOptions[$i][4]) Next FileWriteLine($sLogFile, " - " & UBound($aAllOptions) & " valid display settings were found for console. Saved to .ini for next run") ;If something besides <max> has been selected, then choose that If $sDisplaySelection = "<Max>" Then $aRes[0] = $aAllOptions[0][0] $aRes[1] = $aAllOptions[0][1] $aRes[2] = $aAllOptions[0][2] $aRes[3] = $aAllOptions[0][3] Else ;Find the selected display in the [][4] and use that FileWriteLine($sLogFile, " - " & $sDisplaySelection & " was specified instead of opting for <Max>") For $f = 0 to UBound($aAllOptions) - 1 If $aAllOptions[$f][4] = $sDisplaySelection Then $aRes[0] = $aAllOptions[$f][0] $aRes[1] = $aAllOptions[$f][1] $aRes[2] = $aAllOptions[$f][2] $aRes[3] = $aAllOptions[$f][3] ExitLoop EndIf Next EndIf If $aRes[0] = "" or $aRes[1] = "" or $aRes[2] = "" or $aRes[3] = "" Then FileWriteLine($sLogFile, " - Custom Resolution was not found in Console, Left at default: " & @DesktopWidth & "x" & @DesktopHeight & " at " & @DesktopDepth & "dpi with " & @DesktopRefresh & "Hz refresh rate") Exit EndIf _ChangeScreenResEx(1, $aRes[0], $aRes[1], $aRes[2], $aRes[3]) ;Set the display to the max value Sleep(2000) FileWriteLine($sLogFile, " - Screen resolution set to " & @DesktopWidth & "x" & @DesktopHeight & " at " & @DesktopDepth & "dpi with " & @DesktopRefresh & "Hz refresh rate") ;---------------------------------------------------------------------------------------------------------- ;This is where your automation code would go, take screenshots, interact with Desktop, etc... ;---------------------------------------------------------------------------------------------------------- Exit Else MsgBox(0, "Redirect - Disconnect", "This must be launched via a Remote Desktop Connection") EndIf Exit EndFunc Func _RedirectToConsole($iSessionID) _WinAPI_Wow64EnableWow64FsRedirection(True) ShellExecute(@WindowsDir & '\System32\tscon.exe', $iSessionID & ' /dest:console', '', 'runas') _WinAPI_Wow64EnableWow64FsRedirection(False) EndFunc ;Mod of argumentum code at https://www.autoitscript.com/forum/topic/134679-get-hostname-of-the-client-connected-to-the-terminalserver-session/ Func _WTSQuerySessionInformation($SessionId = -1, $WTSInfoClass = 10, $iReturnAsIs = 0) Local $aResult = DllCall("Wtsapi32.dll", "int", "WTSQuerySessionInformation", "Ptr", 0, "int", $SessionId, "int", $WTSInfoClass, "ptr*", 0, "dword*", 0) If @error Or $aResult[0] = 0 Then Return SetError(1, 0, "") Local $ip = DllStructGetData(DllStructCreate("byte[" & $aResult[5] & "]", $aResult[4]), 1) DllCall("Wtsapi32.dll", "int", "WTSFreeMemory", "ptr", $aResult[4]) If $iReturnAsIs Then Return $ip Switch $WTSInfoClass Case 4 ; We want the WTSSessionId Return Int('0x' & StringTrimRight(StringReverse($ip), 3)) Case 29 ; 1 = Its remote, 0 = physical connection $WTSIsRemoteSession Return Int('0x' & StringTrimRight(StringReverse($ip), 3)) case 16 ; $WTSClientProtocolType Return Int('0x' & StringTrimRight(StringReverse($ip), 3)) Case 14 ; We want the WTSClientAddress If Not (Int(StringLeft($ip, 4)) = 2) Then ; IPv4 $ip = "" Else $ip = Dec(StringMid($ip, 15, 2)) & '.' & Dec(StringMid($ip, 17, 2)) & '.' & Dec(StringMid($ip, 19, 2)) & '.' & Dec(StringMid($ip, 21, 2)) EndIf EndSwitch Return StringReplace(BinaryToString($ip), Chr(0), "") EndFunc ;==>_GetWTSClientName ;============================================================== ;=== Return array containing all valid display resolutions ;=== [#][0] = Width ;=== [#][1] = Height ;=== [#][2] = dpi ;=== [#][3] = Refresh Rate ;=== [#][4] = All values in a user readable string ;=== ;=== Sorted Largest to smallest, dups removed ;=== Origionally from rasim ;=== Modified by: BigDaddyO ;============================================================== Func _EnumDisplaySettingsEx() Local $DEVMODE, $DllRet = 0, $enum = 0 local $aAll[1000][5] ;Create a structure to hold all the items that are returned from the EnumDisplaySettinsEx call $DEVMODE = DllStructCreate("char dmDeviceName[32];ushort dmSpecVersion;ushort dmDriverVersion;short dmSize;" & _ "ushort dmDriverExtra;dword dmFields;short dmOrientation;short dmPaperSize;short dmPaperLength;" & _ "short dmPaperWidth;short dmScale;short dmCopies;short dmDefaultSource;short dmPrintQuality;" & _ "short dmColor;short dmDuplex;short dmYResolution;short dmTTOption;short dmCollate;" & _ "byte dmFormName[32];dword dmBitsPerPel;int dmPelsWidth;dword dmPelsHeight;" & _ "dword dmDisplayFlags;dword dmDisplayFrequency") DllStructSetData($DEVMODE, "dmSize", DllStructGetSize($DEVMODE)) $enum = 0 ;Used to track how many display settings have been added to the $aList Do $DllRet = DllCall("user32.dll", "int", "EnumDisplaySettingsEx", "ptr", 0, "dword", $enum, _ "ptr", DllStructGetPtr($DEVMODE), "dword", 0) $DllRet = $DllRet[0] ;Need to retrieve everything into a 2D array, Sort by the Width in Descending order. ; Then loop through writing them into a single string making sure there are no duplicates and returning as a 1D array $aAll[$enum][0] = Int(DllStructGetData($DEVMODE, "dmPelsWidth")) $aAll[$enum][1] = Int(DllStructGetData($DEVMODE, "dmPelsHeight")) $aAll[$enum][2] = Int(DllStructGetData($DEVMODE, "dmBitsPerPel")) $aAll[$enum][3] = Int(DllStructGetData($DEVMODE, "dmDisplayFrequency")) $aAll[$enum][4] = $aAll[$enum][0] & "x" & $aAll[$enum][1] & ", " & $aAll[$enum][2] & "dpi, " & $aAll[$enum][3] & "Hz" ConsoleWrite("Display Setting = " & $aAll[$enum][4] & @CRLF) $enum += 1 Until $DllRet = 0 $DEVMODE = 0 ;Clear out the DLL Struct before we leave the Func ReDim $aAll[$enum][5] ;Resize to the number of items we actually retrieved _ArraySort($aAll, 1) ;Sort the array so its in the order we want ;Move through the array to get rid of any dups Local $aList[$enum][5] ;This will hold the display settigns without any dups Local $iAdd = 0 ;Keep track of the number of unique records For $d = 0 to $enum - 1 ;Loop through every record returned For $f = 0 to $iAdd ;Loop through only those we already verified as not dups If $aList[$f][4] = $aAll[$d][4] Then ContinueLoop(2) ;We found it, so just skip to the next record EndIf Next ;We will only get to this part of the loop if we didn't find a dup $aList[$iAdd][0] = $aAll[$d][0] $aList[$iAdd][1] = $aAll[$d][1] $aList[$iAdd][2] = $aAll[$d][2] $aList[$iAdd][3] = $aAll[$d][3] $aList[$iAdd][4] = $aAll[$d][4] $iAdd += 1 Next ReDim $aList[$iAdd][5] Return $aList EndFunc ;=============================================================================== ; Function Name: _ChangeScreenResEx() ; Description: Changes the current screen geometry, colour and refresh rate. ; Version: 1.0.0.0 ; Parameter(s): $i_DisplayNum - Display to change, starting at 1 ; $i_Width - Width of the desktop screen in pixels. (horizontal resolution) ; $i_Height - Height of the desktop screen in pixels. (vertical resolution) ; $i_BitsPP - Depth of the desktop screen in bits per pixel. ; $i_RefreshRate - Refresh rate of the desktop screen in hertz. ; Return Value(s): On Success - Screen is adjusted, @ERROR = 0 ; On Failure - sets @ERROR = 1 ; Forum(s): ; Author(s): Original code - psandu.ro, PartyPooper ; Modifications - bobchernow ;=============================================================================== Func _ChangeScreenResEx($i_DisplayNum = 1, $i_Width = @DesktopWidth, $i_Height = @DesktopHeight, $i_BitsPP = @DesktopDepth, $i_RefreshRate = @DesktopRefresh) Local Const $DM_PELSWIDTH = 0x00080000 Local Const $DM_PELSHEIGHT = 0x00100000 Local Const $DM_BITSPERPEL = 0x00040000 Local Const $DM_DISPLAYFREQUENCY = 0x00400000 Local Const $CDS_TEST = 0x00000002 Local Const $CDS_UPDATEREGISTRY = 0x00000001 Local Const $DISP_CHANGE_RESTART = 1 Local Const $DISP_CHANGE_SUCCESSFUL = 0 Local Const $HWND_BROADCAST = 0xffff Local Const $WM_DISPLAYCHANGE = 0x007E If $i_Width = "" Or $i_Width = -1 Then $i_Width = @DesktopWidth; default to current setting If $i_Height = "" Or $i_Height = -1 Then $i_Height = @DesktopHeight; default to current setting If $i_BitsPP = "" Or $i_BitsPP = -1 Then $i_BitsPP = @DesktopDepth; default to current setting If $i_RefreshRate = "" Or $i_RefreshRate = -1 Then $i_RefreshRate = @DesktopRefresh; default to current setting Local $DEVMODE = DllStructCreate("byte[32];int[10];byte[32];int[6]") Local $s_Display $s_Display = "\\.\Display" & $i_DisplayNum Local $B = DllCall("user32.dll", "int", "EnumDisplaySettings", "ptr", 0, "int", 0, "ptr", DllStructGetPtr($DEVMODE)) If @error Then $B = 0 SetError(1) Return $B Else $B = $B[0] EndIf If $B <> 0 Then DllStructSetData($DEVMODE, 2, BitOR($DM_PELSWIDTH, $DM_PELSHEIGHT, $DM_BITSPERPEL, $DM_DISPLAYFREQUENCY), 5) DllStructSetData($DEVMODE, 4, $i_Width, 2) DllStructSetData($DEVMODE, 4, $i_Height, 3) DllStructSetData($DEVMODE, 4, $i_BitsPP, 1) DllStructSetData($DEVMODE, 4, $i_RefreshRate, 5) $B = DllCall("user32.dll", "int", "ChangeDisplaySettingsEx","str", $s_Display, "ptr", DllStructGetPtr($DEVMODE), "hwnd", 0, "dword", $CDS_TEST, "lparam", 0) If @error Then $B = -1 Else $B = $B[0] EndIf Select Case $B = $DISP_CHANGE_RESTART $DEVMODE = "" Return 2 Case $B = $DISP_CHANGE_SUCCESSFUL DllCall("user32.dll", "int", "ChangeDisplaySettingsEx","str", $s_Display, "ptr", DllStructGetPtr($DEVMODE), "hwnd", 0, "dword", $CDS_UPDATEREGISTRY, "lparam", 0) DllCall("user32.dll", "int", "SendMessage", "hwnd", $HWND_BROADCAST, "int", $WM_DISPLAYCHANGE, _ "int", $i_BitsPP, "int", $i_Height * 2 ^ 16 + $i_Width) $DEVMODE = "" Return 1 Case Else $DEVMODE = "" SetError(1) Return $B EndSelect EndIf EndFunc;==>_ChangeScreenResEx Func _ExitApp() Exit EndFunc argumentum and Trong 2 Link to comment Share on other sites More sharing options...
JohnTWI Posted February 4, 2020 Share Posted February 4, 2020 BigDaddyO I have never used a console application. Can you point me additional info on the console? Link to comment Share on other sites More sharing options...
Moderators JLogan3o13 Posted February 4, 2020 Moderators Share Posted February 4, 2020 1 hour ago, JohnTWI said: Can you point me additional info on the console? Yes, the help file. Do a search on console and you will find plenty of material to read. "Profanity is the last vestige of the feeble mind. For the man who cannot express himself forcibly through intellect must do so through shock and awe" - Spencer W. Kimball How to get your question answered on this forum! Link to comment Share on other sites More sharing options...
JohnTWI Posted February 14, 2020 Share Posted February 14, 2020 BigDaddyO Question when I login back on to the Remote server just to monitor how my job is doing then disconnect from the remote session. My AutoIt job dies, is this expected behavior or am not exiting from the remote session properly? Thanks again for your help on this. Link to comment Share on other sites More sharing options...
BigDaddyO Posted February 14, 2020 Author Share Posted February 14, 2020 That is expected behavior. you must launch the redirect to Console script and that will disconnect your RDP session. if you plan on logging in to review, then you will need to have the redirect to console script separate from your normal automation script. 1. launch your automation script 2. launch the Redirect to Console script which will disconnect your RDP session. Link to comment Share on other sites More sharing options...
Two1 Posted November 6, 2020 Share Posted November 6, 2020 On 2/14/2020 at 5:01 PM, BigDaddyO said: That is expected behavior. you must launch the redirect to Console script and that will disconnect your RDP session. if you plan on logging in to review, then you will need to have the redirect to console script separate from your normal automation script. 1. launch your automation script 2. launch the Redirect to Console script which will disconnect your RDP session. Sorry for this noobish question but do I just have to post your code in the editor and save it so the RDP stays open? Sorry again, I'm new with this :( Link to comment Share on other sites More sharing options...
BigDaddyO Posted November 6, 2020 Author Share Posted November 6, 2020 It depends on your automation. If your automation is going to sit there and wait for something to happen then you can use this redirect as a separate application. Launch your automation, then launch this separate redirect app which will close your RDP session and let your automation continue to run. If your automation starts doing stuff over and over as soon as it's launched, then you will need to look into this redirect code and build it into your automation so when you launch it will disconnect RDP and start the actual automation. This will not be a quick plug and play process. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now