Introduction
Ever wanted to keep those Microsoft RDP sessions active and unlocked (both mstsc.exe (normal Microsoft RDP client) and rdcman.exe (Microsoft Remote Desktop Connection Manager)? This will take care of it for you.
Using the Code
Create a console application project in Visual Studio (I used VS 2015 and set it to .NET 4, but I'm sure it should work with others) and replace the code with the code below. You can leave it as a console application and uncomment the Console.Writeline
entries if you like but I personally leave them commented out and go back into the project and change it from "Console Application" to "Windows Forms Application" so that it runs without any sort of window.
Then, if you want to have it start each time you log on, you can just create a registry entry in "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" with a type of String
Value (REG_SZ
) and name it whatever you like and then put the fully qualified file path in the Value
data field (be sure to put quotes around it).
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading
Module Module1
Const PollIntervalSeconds As Integer = 50
Public BoolMouseLoc As Boolean = False
Public BoolNeedToRestore As Boolean
Private Const WmSetfocus As Integer = &H7
Private Const WmMousemove As Integer = &H200
Private Const ALT As Integer = &Ha4
Private Const EXTENDEDKEY As Integer = &H1
Private Const KEYUP As Integer = &H2
Delegate Function EnumWindowDelegate(hWnd As IntPtr, lparam As IntPtr) As Boolean
Private ReadOnly Callback As EnumWindowDelegate = New EnumWindowDelegate(AddressOf EnumWindowProc)
Delegate Function EnumWindowDelegate2(hWnd As IntPtr, lparam As IntPtr) As Boolean
Private ReadOnly Callback2 As EnumWindowDelegate2 = _
New EnumWindowDelegate2(AddressOf EnumWindowProc2)
Private Declare Function EnumWindows Lib "User32.dll" _
(wndenumproc As EnumWindowDelegate, lparam As IntPtr) As Boolean
Private Declare Auto Function EnumChildWindows Lib "User32.dll" _
(windowHandle As IntPtr, wndenumproc2 As EnumWindowDelegate2, lParam As IntPtr) As Boolean
Private Declare Auto Function GetWindowText Lib "User32.dll" _
(hwnd As IntPtr, txt As Byte(), lng As Integer) As Integer
Private Declare Function IsWindowVisible Lib "User32.dll" (hwnd As IntPtr) As Boolean
Private Declare Function IsIconic Lib "User32.dll" (hwnd As IntPtr) As Boolean
Private Declare Function GetWindowTextLengthA Lib "User32.dll" (hwnd As IntPtr) As Integer
Private Declare Auto Function ShowWindow Lib "User32.dll" (hwnd As IntPtr, lng As Integer) _
As Integer
Private Declare Function GetForegroundWindow Lib "User32.dll" () As IntPtr
Private Declare Function SetForegroundWindow Lib "User32.dll" (hwnd As IntPtr) As Integer
Declare Auto Function SendMessage Lib "user32.dll" _
(hWnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
Private Declare Function GetClientRect Lib "User32.dll" _
(hWnd As IntPtr, ByRef lpRect As Rect) As Boolean
<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Sub GetClassName(hWnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Integer)
End Sub
<DllImport("user32.dll")> _
Private Sub keybd_event(bVk As Byte, bScan As Byte, dwFlags As UInteger, dwExtraInfo As Integer)
End Sub
<StructLayout(LayoutKind.Sequential)>
Public Structure Rect
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
Public Overrides Function ToString() As String
Return String.Format("{0},{1},{2},{3}", Left, Top, Right, Bottom)
End Function
End Structure
Sub Main()
Dim storeCurrenthWnd As IntPtr
Do
BoolNeedToRestore = False
BoolMouseLoc = Not BoolMouseLoc
storeCurrenthWnd = GetForegroundWindow()
EnumWindows(Callback, IntPtr.Zero)
If BoolNeedToRestore Then
ShowWindow(storeCurrenthWnd, 5)
keybd_event(CByte(ALT), &H45, EXTENDEDKEY Or 0, 0)
keybd_event(CByte(ALT), &H45, EXTENDEDKEY Or KEYUP, 0)
SetForegroundWindow(storeCurrenthWnd)
End If
GC.Collect()
GC.WaitForPendingFinalizers()
Thread.Sleep(PollIntervalSeconds * 1000)
Loop
End Sub
Private Function EnumWindowProc(hWnd As IntPtr, lparam As IntPtr) As Boolean
Dim myRect As Rect
Dim arrRect(4) As String
Dim coord As Integer
If IsWindowVisible(hWnd) Then
Dim theLength As Integer = GetWindowTextLengthA(hWnd)
Dim theReturn(theLength * 2) As Byte
GetWindowText(hWnd, theReturn, theLength + 1)
Dim theText = ""
For x = 0 To (theLength - 1) * 2
If theReturn(x) <> 0 Then
theText &= Chr(theReturn(x))
End If
Next
If theText.ToLower Like "*remote desktop*" Then
If IsIconic(hWnd) Then
ShowWindow(hWnd, 5)
ShowWindow(hWnd, 9)
BoolNeedToRestore = True
End If
SendMessage(hWnd, WmSetfocus, 0, 0)
GetClientRect(hWnd, myRect)
arrRect = Split(myRect.ToString(), ",")
coord = CInt(CInt(arrRect(2)) / 2) * &H10000 + CInt(CInt(arrRect(3)) / 2)
If BoolMouseLoc Then
SendMessage(hWnd, WmMousemove, 0, 0)
Else
SendMessage(hWnd, WmMousemove, 0, coord)
End If
EnumChildWindows(hWnd, Callback2, IntPtr.Zero)
End If
End If
Return True
End Function
Private Function EnumWindowProc2(hWnd As IntPtr, lparam As IntPtr) As Boolean
Dim myRect As Rect
Dim arrRect(4) As String
Dim coord As Integer
Dim myClassName As String
Dim theLength As Integer = GetWindowTextLengthA(hWnd)
Dim theReturn(theLength * 2) As Byte
GetWindowText(hWnd, theReturn, theLength + 1)
Dim theText = ""
For x = 0 To (theLength - 1) * 2
If theReturn(x) <> 0 Then
theText &= Chr(theReturn(x))
End If
Next
myClassName = GetWindowClass(hWnd)
If Not theText.Trim().Length = 0 And Not theText.ToLower.MultiContains("disconnected") And _
Not myClassName.ToLower.MultiContains("atl:", ".button.", "uicontainerclass", _
".systreeview32.", ".scrollbar.", "opwindowclass", "opcontainerclass") Then
GetClientRect(hWnd, myRect)
arrRect = Split(myRect.ToString(), ",")
coord = CInt(CInt(arrRect(2)) / 2) * &H10000 + CInt(CInt(arrRect(3)) / 2)
If BoolMouseLoc Then
SendMessage(hWnd, WmMousemove, 0, 0)
Else
SendMessage(hWnd, WmMousemove, 0, coord)
End If
End If
Return True
End Function
Public Function GetWindowClass(hwnd As Long) As String
Dim sClassName As New StringBuilder("", 256)
Call GetClassName(hwnd, sClassName, 256)
Return sClassName.ToString
End Function
<Extension>
Public Function MultiContains(str As String, ParamArray values() As String) As Boolean
Return values.Any(Function(val) str.Contains(val))
End Function
End Module
Points of Interest
I am not a professional programmer... so if you see anything that needs to be improved, please let me know.
History
- 3/5/2017
- 3/9/2017
- Added in line comments to the code
- 3/17/2017
- Changed the previous window restoral to only happening when RDP sessions were un-minimized
- Added garbage collection Skipping more windows that don't need to be touched
- 10/21/2017
- Fixed an issue where the focus would not return to the proper window if you had RDP sessions minimized and it needed to un-minimize them.
- Fixed issue that caused a focus problem while using Remote Desktop Connection Manager. Focus would inadvertently go on the server name on the left pane instead of the remote connection window itself from time to time.
- 10/22/2017
- Made small code change based on a user tip relating to the moust location rotation.