/*
This script is run in the server computer from the remote computer
to disconnect the session without locking the server computer
and do not require UAC after the first use
*/
; self elevate
TaskName := RunAsTask()
; get the conexion number
Conn := ActiveSession()
; close the connection
Run, %COMSPEC% /c TSCON %Conn% /dest:console
; functions
RunAsTask() { ; By SKAN, http://ahkscript.org/boards/viewtopic.php?t=4334
Local CmdLine, TaskName, TaskExists, XML, TaskSchd, TaskRoot, RunAsTask
Local TASK_CREATE := 0x2, TASK_LOGON_INTERACTIVE_TOKEN := 3
Try TaskSchd := ComObjCreate( "Schedule.Service" ), TaskSchd.Connect()
, TaskRoot := TaskSchd.GetFolder( "\" )
Catch
Return "", ErrorLevel := 1
CmdLine := ( A_IsCompiled ? "" : """" A_AhkPath """" ) A_Space ( """" A_ScriptFullpath """" )
TaskName := "[RunAsTask] " A_ScriptName " @" SubStr( "000000000" DllCall( "NTDLL\RtlComputeCrc32"
, "Int",0, "WStr",CmdLine, "UInt",StrLen( CmdLine ) * 2, "UInt" ), -9 )
Try RunAsTask := TaskRoot.GetTask( TaskName )
TaskExists := ! A_LastError
If ( not A_IsAdmin and TaskExists ) {
RunAsTask.Run( "" )
ExitApp
}
If ( not A_IsAdmin and not TaskExists ) {
Run *RunAs %CmdLine%, %A_ScriptDir%, UseErrorLevel
ExitApp
}
If ( A_IsAdmin and not TaskExists ) {
XML := "
( LTrim Join
<?xml version=""1.0"" ?><Task xmlns=""http://schemas.microsoft.com/windows/2004/02/mit/task""><Regi
strationInfo /><Triggers /><Principals><Principal id=""Author""><LogonType>InteractiveToken</LogonT
ype><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><MultipleInstancesPolic
y>Parallel</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><
StopIfGoingOnBatteries>false</StopIfGoingOnBatteries><AllowHardTerminate>false</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAva
ilable><IdleSettings><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleS
ettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><
RunOnlyIfIdle>false</RunOnlyIfIdle><DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteApp
Session><UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine><WakeToRun>false</WakeToRun><
ExecutionTimeLimit>PT0S</ExecutionTimeLimit></Settings><Actions Context=""Author""><Exec>
<Command>" ( A_IsCompiled ? A_ScriptFullpath : A_AhkPath ) "</Command>
<Arguments>" ( !A_IsCompiled ? """" A_ScriptFullpath """" : "" ) "</Arguments>
<WorkingDirectory>" A_ScriptDir "</WorkingDirectory></Exec></Actions></Task>
)"
TaskRoot.RegisterTask( TaskName, XML, TASK_CREATE, "", "", TASK_LOGON_INTERACTIVE_TOKEN )
}
Return TaskName, ErrorLevel := 0
}
ActiveSession() {
if ((wtsapi32 := DllCall("LoadLibrary", "Str", "wtsapi32.dll", "Ptr")))
{
if (DllCall("wtsapi32\WTSEnumerateSessionsEx", "Ptr", WTS_CURRENT_SERVER_HANDLE := 0, "UInt*", 1, "UInt", 0, "Ptr*", pSessionInfo, "UInt*", wtsSessionCount))
{
WTS_CONNECTSTATE_CLASS := {0: "WTSActive", 1: "WTSConnected", 2: "WTSConnectQuery", 3: "WTSShadow", 4: "WTSDisconnected", 5: "WTSIdle", 6: "WTSListen", 7: "WTSReset", 8: "WTSDown", 9: "WTSInit"}
cbWTS_SESSION_INFO_1 := A_PtrSize == 8 ? 56 : 32
Loop % wtsSessionCount {
currSessOffset := cbWTS_SESSION_INFO_1 * (A_Index - 1)
ExecEnvId := NumGet(pSessionInfo+0, currSessOffset, "UInt")
currSessOffset += 4
State := NumGet(pSessionInfo+0, currSessOffset, "UInt")
currSessOffset += 4
SessionId := NumGet(pSessionInfo+0, currSessOffset, "UInt")
currSessOffset += A_PtrSize
SessionName := StrGet(NumGet(pSessionInfo+0, currSessOffset, "Ptr"),, A_IsUnicode ? "UTF-16" : "CP0")
currSessOffset += A_PtrSize
HostName := StrGet(NumGet(pSessionInfo+0, currSessOffset, "Ptr"),, A_IsUnicode ? "UTF-16" : "CP0")
currSessOffset += A_PtrSize
UserName := StrGet(NumGet(pSessionInfo+0, currSessOffset, "Ptr"),, A_IsUnicode ? "UTF-16" : "CP0")
currSessOffset += A_PtrSize
DomainName := StrGet(NumGet(pSessionInfo+0, currSessOffset, "Ptr"),, A_IsUnicode ? "UTF-16" : "CP0")
currSessOffset += A_PtrSize
FarmName := StrGet(NumGet(pSessionInfo+0, currSessOffset, "Ptr"),, A_IsUnicode ? "UTF-16" : "CP0")
; MsgBox % "Username: " . UserName . "`r`n" . "State: " . WTS_CONNECTSTATE_CLASS[State] . " (raw state: " . State . ")"
If (UserName = A_UserName && State = 0)
Activa := SessionId
}
DllCall("wtsapi32\WTSFreeMemoryEx", "UInt", WTSTypeSessionInfoLevel1 := 2, "Ptr", pSessionInfo, "UInt", wtsSessionCount)
}
DllCall("FreeLibrary", "Ptr", wtsapi32)
}
Return Activa
}
Some text explaining this would make it a much better answer than it is now. – music2myear – 2019-10-22T02:28:02.877
Because Microsoft sells Windows licenses for single user, it does not allow multiple sessions to run at the same time. When you connect to to a remote server, the server's running session ends and a new session starts, even if it is the same user. In this case at the end of the connection, you need a way to disconnect from the server without ending the server's running session. – gmoises – 2019-10-29T11:48:09.173
You misunderstood me. The answer itself needs to be improved with the additional information added to the answer itself through use of the EDIT button. – music2myear – 2019-10-30T18:58:26.647
The code works very well, I use it everyday to close Remote Desktop connections, I do not feel the need to change the code, but you are encouraged to improve it to fit your specific requirements. – gmoises – 2019-11-06T20:11:14.153
Dude, you aren't reading what I'm writing. We don't need more code if your code works great. What we need is that the current "code lacking context" has some context attached to it. You click the little EDIT button, and then you add some EXPLANATION, not more code, not change what's there, just ADDING something to explain HOW you use this code in a solution. For instance, what the code is, how you implement it, how you run it to solve the problem presented. – music2myear – 2019-11-07T04:39:17.567