Way back in 2007, I wrote this post, which details how to get a Windows XP computer to return to the desktop (console) at the conclusion of an RDP session.
Microsoft’s client operating systems only support one session at a time, meaning that you can have a standard “desktop” session with your Windows PC, or you can connect via Remote Desktop for a remote session. But you can’t do both at once. And when you connect via Remote Desktop, the desktop (console) session is locked, and will remain locked after you log off, until the console user unlocks it via the keyboard.
Needless to say, this is a pain for remote support of Windows users. Normally, I’d have to log in via Remote Desktop, do what I had to do to fix it, then call the end user and tell them that I was done. They’d then unlock their computer and go about their business. But the article I posted in 2007 shows you how to create a shortcut that disconnects the remote user and unlocks the screen, so the end user knows that he or she can use their computer again.
Unfortunately, the batch file from the 2007 post doesn’t work with Windows 7. Microsoft apparently changed the way TSCON works, rendering the old batch file useless. But fear not: I’ve finally figured out how to make it work in Windows 7! Just copy and paste the text below into your favorite text editor (I prefer Notepad++) and save the file with the extension .BAT or .CMD:
tscon.exe 1 /dest:console
exit
Here’s the critical thing: when you’re connected remotely and want to return the user to the desktop session, right-click the batch file and choose “Run as Administrator”. A UAC prompt will appear; click “Yes” and your RDP session will end, and the console session will be unlocked on the remote computer.
With the newer versions of Windows incrementing the session number, I wrote a PowerShell script to find the session number and then run the command – everything with a # afterwards is a comment and can be left in the code
$QueryResults = $queryResults = (quser) -replace ‘\s{2,}’, ‘,’ | ConvertFrom-Csv
#turn results of quser in to rich objects
$RDPid=$QueryResults.sessionname
#Grab the session number
$RDPsession=0
#First session, the console session,
$SessionCount=$Queryresults.count
#The amount of elements in
If ($QueryResults.SESSIONNAME -eq “console”) { #If there are no sessions, just go ahead and exit
Write-Host “There are no sessions to close” #Let the user know there aren’t any sessions
Write-Host “Exiting,,,”
Exit #Exit the script
}
if ($QueryResults.SESSIONNAME -ne “console”) { #Otherwise,…
Write-Host “$SessionCount” ” Sessions in Progress”
#Let the user know how many sessons are in-progress and print the number of RDP sessions
foreach ($element in $queryResults) #For every session withing the rich object that was returned above
# i.e. enumerate the session
{ #Open the for-loop
$RDPcommand=”C:\Windows\System32\tscon.exe “+$RDPid +” /dest:console”
#Set the variable of RDPCommand to include the session number
Write-Host “$RDPsession” ” | ” “$SessionCount” ” | ” “$RDPcommand”
#Let the user know the ordinal number programmatically assigened to session number (strictly for_
#information), the Session number from the rich object, and the command to be run
Write-Host “Trying to close RDP on Console Listerner; Returning to desktop”
#Tell the host that what’s up
#Write-Host “$RDPsession” ” | ” “$RDPcommand”
Invoke-Expression $RDPcommand
#Actually run the command to close the RDP session
$RDPsession++
#Increment the ordinal number of the session
Write-Host “If you are reading this on the desktop, the RDP session was closed”
#Tell the user that, if they can read the message, the procedure was successful
} #Close the for-loop
} #Close the Else statement
Fear not! For posterity, I’m updating the script:
$queryResults = $queryResults = (quser) -replace ‘\s{2,}’, ‘,’ | ConvertFrom-Csv
$RDPid=$QueryResults.sessionname
$RDPsession=0
$SessionCount=$Queryresults.count
“$SessionCount” + ” Sessions in Progress”
foreach ($element in $queryResults)
{
$RDPcommand=”C:\Windows\System32\tscon.exe “+$RDPid +” /dest:console”
“$RDPsession” + ” | ” + “$RDPcommand”
“Trying to close RDP on Console Listerner; Returning to desktop”
Start-Process powershell -Verb runAs -ArgumentList $RDPcommand
$RDPsession++
“If you are reading this on the desktop, the RDP session was closed”
}
Awesome! Thanks for the update! 🙂
See, Jim. Now I feel bad, because I tweaked the logic for working even better:
$queryResults = $queryResults = (quser) -replace ‘\s{2,}’, ‘,’ | ConvertFrom-Csv
$RDPid=$QueryResults.sessionname
$RDPsession =0 #Reset our counter
$SessionCount=$Queryresults.count #Know how many sessions there are, because we’re going to print the output
“$SessionCount” + ” Sessions in Progress” #Format our output
foreach ($element in $queryResults.sessionname) #For every item in our list of sessions
{
$SessionNameIf=$RDPid.get($RDPsession)
if ($SessionNameIf -eq “console”) #Are you on the console? Some logic
{
Write-Host “You’re on the Desktop. Nothing has been changed.” #Gently remind the user and…
exit #Exit
}
else #Otherwise, they’re not on a console session
{
$RDPcommand=”C:\Windows\System32\tscon.exe “+$RDPid.get($RDPSession) +” /dest:console” #Use the element number from our counter
Write-Host “Session $RDPsession” ” | “”Session Name” $RDPid.get($RDPsession) ” | ” “Running $RDPcommand” #Display the session number, and the command to be run
Write-Host “Trying to close RDP on Console Listerner; Returning to desktop” #Inform the User
Start-Process powershell -Verb runAs -ArgumentList $RDPcommand #elevate this PowerShell session
$RDPsession++ #Increment our counter}
Write-Host “If you are reading this on the desktop, the RDP session should have been closed” #Inform the user
}
}
If you have a way of removing all but the most recent, that would be great.
Through testing (and it seemed to work fine, but it didn’t always work), I enhanced the code – for for-each statement kept reading all the sessions as a block – which is good for only one logged on session, but if you have multiple logon sessions, it’ll not work. I fixed that in the latest version.
A minimalistic way:
$RDPcommand=”C:\Windows\System32\tscon.exe “+ (Get-Process -PID $pid).SessionID +” /dest:console”
Start-Process powershell -Verb runAs -WindowStyle hidden -ArgumentList $RDPcommand