How to Programmatically Close Remote Desktop Sessions on Windows Server 2003 via Command Line


2 views

Windows Server 2003 has a default limit of 2 simultaneous Remote Desktop connections (1 console + 1 RDP). When this limit is reached, administrators often get the frustrating "The terminal server has exceeded the maximum number of allowed connections" error. Here's how to solve this without physical access.

The most reliable method is using the built-in query session and reset session commands through Windows Terminal Services commands:

query session /server:SERVERNAME
reset session 2 /server:SERVERNAME

For automation, you can create a batch file:

@echo off
for /f "skip=1 tokens=3" %%I in ('query session /server:SERVERNAME') do (
    if not "%%I"=="console" reset session %%I /server:SERVERNAME
)

For systems with PowerShell remoting enabled (though less common on Server 2003):

Invoke-Command -ComputerName SERVERNAME -ScriptBlock {
    qwinsta | Where-Object { $_ -notmatch 'console|services' } | ForEach-Object { 
        $id = ($_ -split '\s+')[2]
        if($id -ne $null) { rwinsta $id }
    }
}

Here's a more robust VBScript solution that can be scheduled:

Set objShell = CreateObject("WScript.Shell")
strServer = "YOUR_SERVER_NAME"
strCommand = "cmd /c query session /server:" & strServer & " | findstr /i /v ""console services"" | for /f ""tokens=2"" %i in ('more') do @reset session %i /server:" & strServer
objShell.Run strCommand, 0, True

To avoid this situation:

  • Configure Group Policy: Computer Configuration > Administrative Templates > Windows Components > Terminal Services > "Set time limit for disconnected sessions"
  • Edit the registry to increase the connection limit (not recommended for production)
  • Implement scheduled tasks to automatically log off idle sessions

For developers needing programmatic control:

using System.Diagnostics;

public class RDPSessionManager {
    public static void ResetAllSessions(string serverName) {
        ProcessStartInfo psi = new ProcessStartInfo {
            FileName = "cmd.exe",
            Arguments = $"/c query session /server:{serverName} | findstr /i /v \"console services\" | for /f \"tokens=2\" %i in ('more') do @reset session %i /server:{serverName}",
            CreateNoWindow = true,
            UseShellExecute = false
        };
        Process.Start(psi).WaitForExit();
    }
}

Windows Server 2003 has a well-known limitation where only two concurrent Remote Desktop sessions are allowed by default. When these sessions aren't properly closed (often due to network interruptions or improper logoffs), administrators get locked out with the dreaded "Too many users" error.

Here are three effective methods to clear stale sessions remotely:

1. Using query and rwinsta commands

First identify active sessions:

query session /server:SERVERNAME

Then reset specific sessions (replace ID with actual session number):

rwinsta /server:SERVERNAME 1
rwinsta /server:SERVERNAME 2

2. PowerShell Alternative (Requires PSRemoting)

For servers with PowerShell enabled:

$sessions = qwinsta /server:SERVERNAME | ? { $_ -match 'Disc' } | % { ($_ -split '\s+')[2] }
$sessions | % { rwinsta /server:SERVERNAME $_ }

3. Batch Script Automation

Create a cleanup.bat file:

@echo off
for /f "skip=1 tokens=3" %%i in ('query session /server:%1') do (
  if not "%%i"=="console" rwinsta /server:%1 %%i
)

Execute with: cleanup.bat SERVERNAME

Add these registry tweaks (via regedit or reg command):

reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v KeepAliveEnable /t REG_DWORD /d 1 /f
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v KeepAliveInterval /t REG_DWORD /d 30000 /f

For programmatic control in .NET applications:

using System.Diagnostics;

void ResetRemoteSessions(string serverName)
{
    var psi = new ProcessStartInfo("cmd.exe")
    {
        Arguments = $"/c query session /server:{serverName}",
        RedirectStandardOutput = true,
        UseShellExecute = false
    };
    
    var process = Process.Start(psi);
    string output = process.StandardOutput.ReadToEnd();
    
    foreach(var line in output.Split('\n'))
    {
        if(line.Contains("Disc"))
        {
            string sessionId = line.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)[2];
            Process.Start("rwinsta", $"/server:{serverName} {sessionId}").WaitForExit();
        }
    }
}