Desktop switching and connection recovery improvements.

This commit is contained in:
Jared Goodwin 2019-09-13 15:33:22 -07:00
parent f5480effdb
commit 1ce76d3da7
9 changed files with 169 additions and 117 deletions

View File

@ -9,17 +9,23 @@ using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
namespace Remotely.Agent
{
public class Program
{
public static bool IsDebug { get; set; }
static void Main(string[] args)
public static void Main(string[] args)
{
try
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
#if DEBUG
IsDebug = true;
#endif
SetWorkingDirectory();
var argDict = ProcessArgs(args);
@ -33,34 +39,49 @@ namespace Remotely.Agent
return settings;
};
if (argDict.ContainsKey("update"))
{
Updater.CoreUpdate();
}
if (OSUtils.IsWindows)
if (!IsDebug && OSUtils.IsWindows)
{
#if DEBUG
IsDebug = true;
DeviceSocket.Connect();
#else
ServiceBase.Run(new WindowsService());
#endif
}
else
{
DeviceSocket.Connect();
Task.Run(() =>
{
ServiceBase.Run(new WindowsService());
});
}
while (true)
{
System.Threading.Thread.Sleep(1000);
}
HandleConnection();
}
catch (Exception ex)
{
Logger.Write(ex);
throw;
}
}
private static void HandleConnection()
{
while (true)
{
try
{
if (!DeviceSocket.IsConnected)
{
var waitTime = new Random().Next(1000, 30000);
Logger.Write($"Websocket closed. Reconnecting in {waitTime / 1000} seconds...");
Task.Delay(waitTime).Wait();
DeviceSocket.Connect().Wait();
}
}
catch (Exception ex)
{
Logger.Write(ex);
HandleConnection();
}
Thread.Sleep(1000);
}
}
@ -77,7 +98,6 @@ namespace Remotely.Agent
}
}
}
private static Dictionary<string,string> ProcessArgs(string[] args)
{
var argDict = new Dictionary<string, string>();

View File

@ -24,7 +24,7 @@ namespace Remotely.Agent.Services
private static HubConnection HubConnection { get; set; }
public static async void Connect()
public static async Task Connect()
{
ConnectionInfo = Utilities.GetConnectionInfo();
@ -32,8 +32,6 @@ namespace Remotely.Agent.Services
.WithUrl(ConnectionInfo.Host + "/DeviceHub")
.Build();
HubConnection.Closed += HubConn_Closed;
RegisterMessageHandlers(HubConnection);
await HubConnection.StartAsync();
@ -63,6 +61,8 @@ namespace Remotely.Agent.Services
HeartbeatTimer.Start();
}
public static bool IsConnected => HubConnection?.State == HubConnectionState.Connected;
public static void SendHeartbeat()
{
var currentInfo = Device.Create(ConnectionInfo);
@ -160,12 +160,6 @@ namespace Remotely.Agent.Services
SendHeartbeat();
}
private static async Task HubConn_Closed(Exception arg)
{
await Task.Delay(new Random().Next(5000, 30000));
Connect();
}
private static void RegisterMessageHandlers(HubConnection hubConnection)
{
hubConnection.On("ExecuteCommand", (async (string mode, string command, string commandID, string senderConnectionID) =>

View File

@ -14,6 +14,7 @@ namespace Remotely.Agent.Services
{
public WindowsService()
{
CanHandleSessionChangeEvent = true;
InitializeComponent();
}
@ -28,7 +29,6 @@ namespace Remotely.Agent.Services
var subkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", true);
subkey.SetValue("SoftwareSASGeneration", "3", Microsoft.Win32.RegistryValueKind.DWord);
}
DeviceSocket.Connect();
}
catch (Exception ex)
{
@ -57,5 +57,20 @@ namespace Remotely.Agent.Services
throw;
}
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
Logger.Write($"Session changed. Reason: {changeDescription.Reason}");
if (changeDescription.Reason == SessionChangeReason.ConsoleDisconnect ||
changeDescription.Reason == SessionChangeReason.RemoteDisconnect)
{
foreach (var screenCaster in Process.GetProcessesByName("Remotely_ScreenCast"))
{
Logger.Write($"Session changed. Kill process ID {screenCaster.Id}.");
screenCaster.Kill();
}
}
base.OnSessionChange(changeDescription);
}
}
}

View File

@ -10,6 +10,8 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Remotely.Shared.Services;
using Remotely.Shared.Win32;
namespace Remotely.ScreenCast.Core.Capture
{
@ -61,24 +63,27 @@ namespace Remotely.ScreenCast.Core.Capture
};
// TODO: SetThreadDesktop causes issues with input after switching.
//var desktopName = Win32Interop.GetCurrentDesktop();
var desktopName = string.Empty;
if (OSUtils.IsWindows)
{
desktopName = Win32Interop.GetCurrentDesktop();
}
while (!viewer.DisconnectRequested)
{
try
{
// TODO: SetThreadDesktop causes issues with input after switching.
//var currentDesktopName = Win32Interop.GetCurrentDesktop();
//if (desktopName.ToLower() != currentDesktopName.ToLower())
//{
// desktopName = currentDesktopName;
// Logger.Write($"Switching to desktop {desktopName} in ScreenCaster.");
// var inputDesktop = Win32Interop.OpenInputDesktop();
// User32.SetThreadDesktop(inputDesktop);
// User32.CloseDesktop(inputDesktop);
// continue;
//}
var currentDesktopName = Win32Interop.GetCurrentDesktop();
if (desktopName.ToLower() != currentDesktopName.ToLower())
{
desktopName = currentDesktopName;
Logger.Write($"Switching to desktop {desktopName} in ScreenCaster.");
Win32Interop.SwitchToInputDesktop();
continue;
}
while (viewer.PendingFrames > 10 || conductor.IsSwitchingDesktops)
while (viewer.PendingFrames > 10)
{
await Task.Delay(1);
}
@ -135,6 +140,5 @@ namespace Remotely.ScreenCast.Core.Capture
}
}
}
}

View File

@ -11,6 +11,7 @@ using System.Diagnostics;
using System.Drawing;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
namespace Remotely.ScreenCast.Linux
{
@ -34,7 +35,7 @@ namespace Remotely.ScreenCast.Linux
Conductor.IdleTimer.Start();
while (true)
{
System.Threading.Thread.Sleep(100);
Thread.Sleep(1000);
}
}
catch (Exception ex)

View File

@ -11,6 +11,7 @@ namespace Remotely.ScreenCast.Win.Input
{
public uint SendLeftMouseDown(double percentX, double percentY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
var normalizedX = xyPercent.Item1 * 65535D;
@ -21,6 +22,7 @@ namespace Remotely.ScreenCast.Win.Input
}
public uint SendLeftMouseUp(double percentX, double percentY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
var normalizedX = xyPercent.Item1 * 65535D;
@ -31,6 +33,7 @@ namespace Remotely.ScreenCast.Win.Input
}
public uint SendRightMouseDown(double percentX, double percentY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
var normalizedX = xyPercent.Item1 * 65535D;
@ -41,6 +44,7 @@ namespace Remotely.ScreenCast.Win.Input
}
public uint SendRightMouseUp(double percentX, double percentY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
var normalizedX = xyPercent.Item1 * 65535D;
@ -51,6 +55,7 @@ namespace Remotely.ScreenCast.Win.Input
}
public uint SendMouseMove(double percentX, double percentY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
var normalizedX = xyPercent.Item1 * 65535D;
@ -61,6 +66,7 @@ namespace Remotely.ScreenCast.Win.Input
}
public uint SendMouseWheel(int deltaY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
if (deltaY < 0)
{
deltaY = -120;
@ -75,6 +81,7 @@ namespace Remotely.ScreenCast.Win.Input
}
public void SendKeyDown(string key, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var keyCode = ConvertJavaScriptKeyToVirtualKey(key);
var union = new InputUnion()
{
@ -91,6 +98,7 @@ namespace Remotely.ScreenCast.Win.Input
}
public void SendKeyUp(string key, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var keyCode = ConvertJavaScriptKeyToVirtualKey(key);
var union = new InputUnion()
{

View File

@ -41,33 +41,6 @@ namespace Remotely.ScreenCast.Win
}
}
public static async Task HandleConnection(Conductor conductor)
{
while (true)
{
var desktopName = Win32Interop.GetCurrentDesktop();
if (desktopName.ToLower() != conductor.CurrentDesktopName.ToLower() && conductor.Viewers.Count > 0)
{
conductor.CurrentDesktopName = desktopName;
Logger.Write($"Switching desktops to {desktopName}.");
conductor.IsSwitchingDesktops = true;
// TODO: SetThreadDesktop causes issues with input after switching.
//var inputDesktop = Win32Interop.OpenInputDesktop();
//User32.SetThreadDesktop(inputDesktop);
//User32.CloseDesktop(inputDesktop);
conductor.Connection.InvokeAsync("SwitchingDesktops", conductor.Viewers.Keys.ToList()).Wait();
var result = Win32Interop.OpenInteractiveProcess(Assembly.GetExecutingAssembly().Location + $" -mode {conductor.Mode.ToString()} -requester {conductor.RequesterID} -serviceid {conductor.ServiceID} -deviceid {conductor.DeviceID} -host {conductor.Host} -relaunch true -desktop {desktopName} -viewers {String.Join(",", conductor.Viewers.Keys.ToList())}", desktopName, true, out _);
if (!result)
{
Logger.Write($"Desktop switch to {desktopName} failed.");
conductor.CasterSocket.SendConnectionFailedToViewers(conductor.Viewers.Keys.ToList()).Wait();
}
conductor.Viewers.Clear();
}
await Task.Delay(100);
}
}
public static void Main(string[] args)
{
try
@ -88,7 +61,7 @@ namespace Remotely.ScreenCast.Win
CheckForRelaunch();
Conductor.IdleTimer = new IdleTimer(Conductor.Viewers);
Conductor.IdleTimer.Start();
HandleConnection(Conductor).Wait();
HandleConnection(Conductor);
}
catch (Exception ex)
{
@ -149,17 +122,43 @@ namespace Remotely.ScreenCast.Win
private static void Conductor_ClipboardTransferred(object sender, string transferredText)
{
var thread = new Thread(() => {
var thread = new Thread(() =>
{
Clipboard.SetText(transferredText);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Logger.Write((Exception)e.ExceptionObject);
}
private static void HandleConnection(Conductor conductor)
{
while (true)
{
var desktopName = Win32Interop.GetCurrentDesktop();
if (desktopName.ToLower() != conductor.CurrentDesktopName.ToLower() && conductor.Viewers.Count > 0)
{
conductor.CurrentDesktopName = desktopName;
Logger.Write($"Switching desktops to {desktopName}.");
// TODO: SetThreadDesktop causes issues with input after switching.
Win32Interop.SwitchToInputDesktop();
//conductor.IsSwitchingDesktops = true;
//conductor.Connection.InvokeAsync("SwitchingDesktops", conductor.Viewers.Keys.ToList()).Wait();
//var result = Win32Interop.OpenInteractiveProcess(Assembly.GetExecutingAssembly().Location + $" -mode {conductor.Mode.ToString()} -requester {conductor.RequesterID} -serviceid {conductor.ServiceID} -deviceid {conductor.DeviceID} -host {conductor.Host} -relaunch true -desktop {desktopName} -viewers {String.Join(",", conductor.Viewers.Keys.ToList())}", desktopName, true, out _);
//if (!result)
//{
// Logger.Write($"Desktop switch to {desktopName} failed.");
// conductor.CasterSocket.SendConnectionFailedToViewers(conductor.Viewers.Keys.ToList()).Wait();
//}
//conductor.Viewers.Clear();
}
Thread.Sleep(1000);
}
}
private static async void ScreenCastInitiated(object sender, ScreenCastRequest screenCastRequest)
{
ICapturer capturer;

View File

@ -1 +1 @@
2019.08.21.1359
2019.09.13.1515

View File

@ -12,6 +12,54 @@ namespace Remotely.Shared.Win32
{
public class Win32Interop
{
public static string GetCurrentDesktop()
{
var inputDesktop = OpenInputDesktop();
byte[] deskBytes = new byte[256];
uint lenNeeded;
var success = GetUserObjectInformationW(inputDesktop, UOI_NAME, deskBytes, 256, out lenNeeded);
if (!success)
{
CloseDesktop(inputDesktop);
return "Default";
}
var desktopName = Encoding.Unicode.GetString(deskBytes.Take((int)lenNeeded).ToArray()).Replace("\0", "");
CloseDesktop(inputDesktop);
return desktopName;
}
public static uint GetRDPSession()
{
IntPtr ppSessionInfo = IntPtr.Zero;
Int32 count = 0;
Int32 retval = WTSAPI32.WTSEnumerateSessions(WTSAPI32.WTS_CURRENT_SERVER_HANDLE, 0, 1, ref ppSessionInfo, ref count);
Int32 dataSize = Marshal.SizeOf(typeof(WTSAPI32.WTS_SESSION_INFO));
var sessList = new List<WTSAPI32.WTS_SESSION_INFO>();
Int64 current = (Int64)ppSessionInfo;
if (retval != 0)
{
for (int i = 0; i < count; i++)
{
WTSAPI32.WTS_SESSION_INFO sessInf = (WTSAPI32.WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTSAPI32.WTS_SESSION_INFO));
current += dataSize;
sessList.Add(sessInf);
}
}
uint retVal = 0;
var rdpSession = sessList.Find(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0);
if (sessList.Exists(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0))
{
retVal = (uint)rdpSession.SessionID;
}
return retVal;
}
public static IntPtr OpenInputDesktop()
{
return User32.OpenInputDesktop(0, false, ACCESS_MASK.GENERIC_ALL);
}
public static bool OpenInteractiveProcess(string applicationName, string desktopName, bool hiddenWindow, out PROCESS_INFORMATION procInfo)
{
uint winlogonPid = 0;
@ -90,54 +138,17 @@ namespace Remotely.Shared.Win32
return result;
}
public static uint GetRDPSession()
{
IntPtr ppSessionInfo = IntPtr.Zero;
Int32 count = 0;
Int32 retval = WTSAPI32.WTSEnumerateSessions(WTSAPI32.WTS_CURRENT_SERVER_HANDLE, 0, 1, ref ppSessionInfo, ref count);
Int32 dataSize = Marshal.SizeOf(typeof(WTSAPI32.WTS_SESSION_INFO));
var sessList = new List<WTSAPI32.WTS_SESSION_INFO>();
Int64 current = (Int64)ppSessionInfo;
if (retval != 0)
{
for (int i = 0; i < count; i++)
{
WTSAPI32.WTS_SESSION_INFO sessInf = (WTSAPI32.WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTSAPI32.WTS_SESSION_INFO));
current += dataSize;
sessList.Add(sessInf);
}
}
uint retVal = 0;
var rdpSession = sessList.Find(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0);
if (sessList.Exists(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0))
{
retVal = (uint)rdpSession.SessionID;
}
return retVal;
}
public static IntPtr OpenInputDesktop()
{
return User32.OpenInputDesktop(0, false, ACCESS_MASK.GENERIC_ALL);
}
public static string GetCurrentDesktop()
{
var inputDesktop = OpenInputDesktop();
byte[] deskBytes = new byte[256];
uint lenNeeded;
var success = GetUserObjectInformationW(inputDesktop, UOI_NAME, deskBytes, 256, out lenNeeded);
if (!success)
{
CloseDesktop(inputDesktop);
return "Default";
}
var desktopName = Encoding.Unicode.GetString(deskBytes.Take((int)lenNeeded).ToArray()).Replace("\0", "");
CloseDesktop(inputDesktop);
return desktopName;
}
public static void SetMonitorState(MonitorState state)
{
User32.SendMessage(0xFFFF, 0x112, 0xF170, (int)state);
}
public static void SwitchToInputDesktop()
{
var inputDesktop = OpenInputDesktop();
SwitchDesktop(inputDesktop);
SetThreadDesktop(inputDesktop);
CloseDesktop(inputDesktop);
}
}
}