diff --git a/Agent/Program.cs b/Agent/Program.cs index f664533c..98d6d67f 100644 --- a/Agent/Program.cs +++ b/Agent/Program.cs @@ -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 ProcessArgs(string[] args) { var argDict = new Dictionary(); diff --git a/Agent/Services/DeviceSocket.cs b/Agent/Services/DeviceSocket.cs index 4255a141..f9be84c8 100644 --- a/Agent/Services/DeviceSocket.cs +++ b/Agent/Services/DeviceSocket.cs @@ -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) => diff --git a/Agent/Services/WindowsService.cs b/Agent/Services/WindowsService.cs index bddf3bba..a5df9a1a 100644 --- a/Agent/Services/WindowsService.cs +++ b/Agent/Services/WindowsService.cs @@ -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); + } } } diff --git a/ScreenCast.Core/Capture/ScreenCaster.cs b/ScreenCast.Core/Capture/ScreenCaster.cs index f6ee7757..39e520e9 100644 --- a/ScreenCast.Core/Capture/ScreenCaster.cs +++ b/ScreenCast.Core/Capture/ScreenCaster.cs @@ -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 } } - } } diff --git a/ScreenCast.Linux/Program.cs b/ScreenCast.Linux/Program.cs index a9b0436e..73c6dba2 100644 --- a/ScreenCast.Linux/Program.cs +++ b/ScreenCast.Linux/Program.cs @@ -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) diff --git a/ScreenCast.Win/Input/WinInput.cs b/ScreenCast.Win/Input/WinInput.cs index 4aaf809b..e072d100 100644 --- a/ScreenCast.Win/Input/WinInput.cs +++ b/ScreenCast.Win/Input/WinInput.cs @@ -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() { diff --git a/ScreenCast.Win/Program.cs b/ScreenCast.Win/Program.cs index b989a4de..96d82a2b 100644 --- a/ScreenCast.Win/Program.cs +++ b/ScreenCast.Win/Program.cs @@ -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; diff --git a/Server/CurrentVersion.txt b/Server/CurrentVersion.txt index d56fd801..a22ac769 100644 --- a/Server/CurrentVersion.txt +++ b/Server/CurrentVersion.txt @@ -1 +1 @@ -2019.08.21.1359 +2019.09.13.1515 diff --git a/Shared/Win32/Win32Interop.cs b/Shared/Win32/Win32Interop.cs index 3ab54a78..fe70e465 100644 --- a/Shared/Win32/Win32Interop.cs +++ b/Shared/Win32/Win32Interop.cs @@ -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(); + 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(); - 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); + } } }