// retrieve the next image public RemoteSessionImage GetNextUpdate(int imageIdx, int?waitDuration = null) { RemoteSessionImage image = null; lock (_imageEventLock) { try { // retrieve the next available image from cache, up to the latest received if (imageIdx < _lastReceivedImageIdx) { for (var idx = imageIdx + 1; idx <= _lastReceivedImageIdx; idx++) { image = GetCachedUpdate(idx); if (image != null) { break; } } } // if no image is available and a wait duration is specified, wait for a new image if (image == null && waitDuration.HasValue) { Trace.TraceInformation("Waiting for new image, remote session {0}", RemoteSession.Id); _imageEventPending = true; if (waitDuration.Value > 0) { // wait for the specified time if (Monitor.Wait(_imageEventLock, waitDuration.Value)) { image = GetCachedUpdate(_lastReceivedImageIdx); } } else { // wait indefinitely if (Monitor.Wait(_imageEventLock)) { image = GetCachedUpdate(_lastReceivedImageIdx); } } _imageEventPending = false; Monitor.Pulse(_imageEventLock); } } catch (Exception exc) { Trace.TraceError("Failed to retrieve next update from index {0}, remote session {1} ({2})", imageIdx, RemoteSession.Id, exc); } } return(image); }
public void SendImage(RemoteSessionImage image) { if (!BinaryMode) { Send(GetImageText(image) + ";"); } else { Send(GetImageBytes(image)); } }
private string GetImageText(RemoteSessionImage image) { return (image.Idx + "," + image.PosX + "," + image.PosY + "," + image.Width + "," + image.Height + "," + image.Format.ToString().ToLower() + "," + image.Quality + "," + image.Fullscreen.ToString().ToLower() + "," + Convert.ToBase64String(image.Data)); }
public byte[] TakeScreenshot(Guid connectionId) { try { HttpContext.Current.Application.Lock(); var remoteSessions = (IDictionary <Guid, RemoteSession>)HttpContext.Current.Application[HttpApplicationStateVariables.RemoteSessions.ToString()]; if (!remoteSessions.ContainsKey(connectionId)) { throw new Exception(string.Format("connection {0} not found", connectionId)); } else { var remoteSession = remoteSessions[connectionId]; RemoteSessionImage screenshot = null; // one screenshot at a time if (remoteSession.Manager.ScreenshotEventLock == null) { remoteSession.Manager.ScreenshotEventLock = new object(); // request a screenshot (async) and wait it (up to 10 seconds) lock (remoteSession.Manager.ScreenshotEventLock) { remoteSession.Manager.SendCommand(RemoteSessionCommand.TakeScreenshot); remoteSession.Manager.ScreenshotEventPending = true; if (Monitor.Wait(remoteSession.Manager.ScreenshotEventLock, 10000)) { screenshot = remoteSession.Manager.GetCachedUpdate(remoteSession.Manager.ScreenshotImageIdx); } remoteSession.Manager.ScreenshotEventPending = false; Monitor.Pulse(remoteSession.Manager.ScreenshotEventLock); } remoteSession.Manager.ScreenshotEventLock = null; } return(screenshot?.Data); } } catch (Exception exc) { Trace.TraceError("Failed to take screenshot for connection {0} ({1})", connectionId, exc); throw; } finally { HttpContext.Current.Application.UnLock(); } }
public void ProcessImage(RemoteSessionImage image) { if (!BinaryMode) { Send(GetImageText(image) + ";"); } else { using (var memoryStream = new MemoryStream()) { var bytes = GetImageBytes(image); memoryStream.Write(BitConverter.GetBytes(bytes.Length), 0, 4); memoryStream.Write(bytes, 0, bytes.Length); Send(memoryStream.ToArray()); } } }
// retrieve a cached image public RemoteSessionImage GetCachedUpdate(int imageIdx) { RemoteSessionImage image = null; try { var imageObj = _cache["remoteSessionImage_" + RemoteSession.Id + "_" + imageIdx]; if (imageObj != null) { image = (RemoteSessionImage)imageObj; Trace.TraceInformation("Retrieved image {0} ({1}) from cache, remote session {2}", imageIdx, (image.Fullscreen ? "screen" : "region"), RemoteSession.Id); } } catch (Exception exc) { Trace.TraceError("Failed to retrieve image {0} from cache, remote session {1} ({2})", imageIdx, RemoteSession.Id, exc); } return(image); }
private byte[] GetImageBytes(RemoteSessionImage image) { using (var memoryStream = new MemoryStream()) { // tag (4 bytes) memoryStream.Write(BitConverter.GetBytes(0), 0, 4); // info (32 bytes) memoryStream.Write(BitConverter.GetBytes(image.Idx), 0, 4); memoryStream.Write(BitConverter.GetBytes(image.PosX), 0, 4); memoryStream.Write(BitConverter.GetBytes(image.PosY), 0, 4); memoryStream.Write(BitConverter.GetBytes(image.Width), 0, 4); memoryStream.Write(BitConverter.GetBytes(image.Height), 0, 4); memoryStream.Write(BitConverter.GetBytes((int)image.Format), 0, 4); memoryStream.Write(BitConverter.GetBytes(image.Quality), 0, 4); memoryStream.Write(BitConverter.GetBytes(image.Fullscreen ? 1 : 0), 0, 4); // data memoryStream.Write(image.Data, 0, image.Data.Length); return(memoryStream.ToArray()); } }
// new image private void ProcessUpdate(byte[] data) { try { if (data.Length <= 36) { throw new Exception("invalid image data"); } // image info (8 items, 32 bits each => 8 * 4 = 32 bytes) var imgInfo = new byte[32]; Array.Copy(data, 4, imgInfo, 0, 32); // CAUTION! if the remote session is reconnected (i.e.: browser resize), a new instance of wfreerdp is spawned // the image index can no longer be handled by wfreerdp, this must be done by the remote session manager // the image index provided by wfreerdp is relative to its own instance, if needed var image = new RemoteSessionImage { //Idx = BitConverter.ToInt32(imgInfo, 0), Idx = ++_imageIdx, PosX = BitConverter.ToInt32(imgInfo, 4), PosY = BitConverter.ToInt32(imgInfo, 8), Width = BitConverter.ToInt32(imgInfo, 12), Height = BitConverter.ToInt32(imgInfo, 16), Format = (ImageFormat)BitConverter.ToInt32(imgInfo, 20), Quality = BitConverter.ToInt32(imgInfo, 24), Fullscreen = BitConverter.ToInt32(imgInfo, 28) == 1, Data = new byte[data.Length - 36] }; Array.Copy(data, 36, image.Data, 0, data.Length - 36); // cache the image, even if using websocket (used to retrieve the mouse cursor on IE) _cache.Insert( "remoteSessionImage_" + RemoteSession.Id + "_" + image.Idx, image, null, DateTime.Now.AddMilliseconds(_imageCacheDuration), Cache.NoSlidingExpiration); Trace.TraceInformation("Received image {0} ({1}), remote session {2}", image.Idx, (image.Fullscreen ? "screen" : "region"), RemoteSession.Id); // while a fullscreen update is pending, discard all updates; the fullscreen will replace all of them if (FullscreenEventPending) { if (!image.Fullscreen) { Trace.TraceInformation("Discarding image {0} (region) as a fullscreen update is pending, remote session {1}", image.Idx, RemoteSession.Id); return; } else { Trace.TraceInformation("Fullscreen update received, resuming image(s) processing, remote session {0}", RemoteSession.Id); FullscreenEventPending = false; // screenshot request if (ScreenshotEventLock != null) { lock (ScreenshotEventLock) { // screenshot image index ScreenshotImageIdx = image.Idx; // if waiting for a screenshot, signal the reception if (ScreenshotEventPending) { ScreenshotEventPending = false; Monitor.Pulse(ScreenshotEventLock); } } } } } // HTML5 client(s) if (WebSockets.Count > 0) { Trace.TraceInformation("Sending image {0} ({1}) on websocket(s), remote session {2}", image.Idx, (image.Fullscreen ? "screen" : "region"), RemoteSession.Id); foreach (var webSocket in WebSockets) { webSocket.SendImage(image); } } // HTML4 client(s) lock (_imageEventLock) { // last received image index _lastReceivedImageIdx = image.Idx; // if waiting for a new image, signal the reception if (_imageEventPending) { _imageEventPending = false; Monitor.Pulse(_imageEventLock); } } } catch (Exception exc) { Trace.TraceError("Failed to process update, remote session {0} ({1})", RemoteSession.Id, exc); } }
// new image private void ProcessUpdate(byte[] data) { try { if (data.Length <= 36) { throw new Exception("invalid image data"); } // image info (8 items, 32 bits each => 8 * 4 = 32 bytes) var imgInfo = new byte[32]; Array.Copy(data, 4, imgInfo, 0, 32); var image = new RemoteSessionImage { Idx = BitConverter.ToInt32(imgInfo, 0), PosX = BitConverter.ToInt32(imgInfo, 4), PosY = BitConverter.ToInt32(imgInfo, 8), Width = BitConverter.ToInt32(imgInfo, 12), Height = BitConverter.ToInt32(imgInfo, 16), Format = (ImageFormat)BitConverter.ToInt32(imgInfo, 20), Quality = BitConverter.ToInt32(imgInfo, 24), Fullscreen = BitConverter.ToInt32(imgInfo, 28) == 1, Data = new byte[data.Length - 36] }; Array.Copy(data, 36, image.Data, 0, data.Length - 36); // cache the image, even if using websocket (used to retrieve the mouse cursor on IE) _imageCache.Insert( "remoteSessionImage_" + RemoteSession.Id + "_" + image.Idx, image, null, DateTime.Now.AddMilliseconds(_imageCacheDuration), Cache.NoSlidingExpiration); Trace.TraceInformation("Received image {0} ({1}), remote session {2}", image.Idx, (image.Fullscreen ? "screen" : "region"), RemoteSession.Id); // while a fullscreen update is pending, discard all updates; the fullscreen will replace all of them if (FullscreenEventPending) { if (!image.Fullscreen) { Trace.TraceInformation("Discarding image {0} (region) as a fullscreen update is pending, remote session {1}", image.Idx, RemoteSession.Id); return; } else { Trace.TraceInformation("Fullscreen update received, resuming image(s) processing, remote session {0}", RemoteSession.Id); FullscreenEventPending = false; } } // if using websocket(s), send the image if (WebSockets.Count > 0) { Trace.TraceInformation("Sending image {0} ({1}) on websocket(s), remote session {2}", image.Idx, (image.Fullscreen ? "screen" : "region"), RemoteSession.Id); foreach (var webSocket in WebSockets) { if (!webSocket.BinaryMode) { webSocket.Send( image.Idx + "," + image.PosX + "," + image.PosY + "," + image.Width + "," + image.Height + "," + image.Format.ToString().ToLower() + "," + image.Quality + "," + image.Fullscreen.ToString().ToLower() + "," + Convert.ToBase64String(image.Data)); } else { webSocket.Send(data); } } } // otherwise, it will be retrieved later else { lock (_imageEventLock) { // last received image index _lastReceivedImageIdx = image.Idx; // if waiting for a new image, signal the reception if (_imageEventPending) { _imageEventPending = false; Monitor.Pulse(_imageEventLock); } } } } catch (Exception exc) { Trace.TraceError("Failed to process update, remote session {0} ({1})", RemoteSession.Id, exc); } }
// new image public void ProcessUpdate(string data) { try { var imgParts = data.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); if (imgParts.Length != 9) { throw new Exception("image can't be deserialized"); } var image = new RemoteSessionImage { Idx = int.Parse(imgParts[0]), PosX = int.Parse(imgParts[1]), PosY = int.Parse(imgParts[2]), Width = int.Parse(imgParts[3]), Height = int.Parse(imgParts[4]), Format = (ImageFormat)Enum.Parse(typeof(ImageFormat), imgParts[5]), Quality = int.Parse(imgParts[6]), Base64Data = imgParts[7].Replace("\n", ""), Fullscreen = imgParts[8] == "1" }; Trace.TraceInformation("Received image {0} ({1}), remote session {2}", image.Idx, (image.Fullscreen ? "screen" : "region"), RemoteSession.Id); // while a fullscreen update is pending, discard all updates; the fullscreen will replace all of them if (_fullscreenPending) { if (!image.Fullscreen) { Trace.TraceInformation("Discarding image {0} (region) as a fullscreen update is pending, remote session {1}", image.Idx, RemoteSession.Id); return; } else { Trace.TraceInformation("Fullscreen update received, resuming image(s) processing, remote session {0}", RemoteSession.Id); _fullscreenPending = false; } } // if using a websocket, send the image if (WebSocket != null) { if (WebSocket.IsAvailable) { Trace.TraceInformation("Sending image {0} ({1}) on websocket, remote session {2}", image.Idx, (image.Fullscreen ? "screen" : "region"), RemoteSession.Id); WebSocket.Send( image.Idx + "," + image.PosX + "," + image.PosY + "," + image.Width + "," + image.Height + "," + image.Format.ToString().ToLower() + "," + image.Quality + "," + image.Base64Data + "," + image.Fullscreen.ToString().ToLower()); } else { Trace.TraceInformation("Websocket is unavailable (connection closed by client?), remote session {0}, status: {1}", RemoteSession.Id, RemoteSession.State); } } // otherwise cache it (will be retrieved later) else { lock (ImageEventLock) { _imageCache.Insert( "remoteSessionImage_" + RemoteSession.Id + "_" + image.Idx, image, null, DateTime.Now.AddMilliseconds(_imageCacheDuration), Cache.NoSlidingExpiration); // last received image index _lastReceivedImageIdx = image.Idx; // if waiting for a new image, signal the reception if (ImageEventPending) { ImageEventPending = false; Monitor.Pulse(ImageEventLock); } } } } catch (Exception exc) { Trace.TraceError("Failed to process update {0}, remote session {1} ({2})", data, RemoteSession.Id, exc); } }
// new image private void ProcessUpdate(byte[] data) { try { if (data.Length <= 36) { throw new Exception("invalid image data"); } // image info (8 items, 32 bits each => 8 * 4 = 32 bytes) var imgInfo = new byte[32]; Array.Copy(data, 4, imgInfo, 0, 32); // CAUTION! if the remote session is reconnected (i.e.: browser resize), a new instance of wfreerdp is spawned // the image index can no longer be handled by wfreerdp, this must be done by the remote session manager // the image index provided by wfreerdp is relative to its own instance, if needed var image = new RemoteSessionImage { //Idx = BitConverter.ToInt32(imgInfo, 0), Idx = _imageIdx == int.MaxValue ? 1 : ++_imageIdx, PosX = BitConverter.ToInt32(imgInfo, 4), PosY = BitConverter.ToInt32(imgInfo, 8), Width = BitConverter.ToInt32(imgInfo, 12), Height = BitConverter.ToInt32(imgInfo, 16), Format = (ImageFormat)BitConverter.ToInt32(imgInfo, 20), Quality = BitConverter.ToInt32(imgInfo, 24), Fullscreen = BitConverter.ToInt32(imgInfo, 28) == 1, Data = new byte[data.Length - 36] }; Array.Copy(data, 36, image.Data, 0, data.Length - 36); // cache the image, even if using websocket (used to retrieve the mouse cursor on IE) _cache.Insert( "remoteSessionImage_" + RemoteSession.Id + "_" + image.Idx, image, null, DateTime.Now.AddMilliseconds(_imageCacheDuration), Cache.NoSlidingExpiration); Trace.TraceInformation("Received image {0} ({1}), remote session {2}", image.Idx, (image.Fullscreen ? "screen" : "region"), RemoteSession.Id); // a fullscreen update was requested if (FullscreenEventPending && image.Fullscreen) { Trace.TraceInformation("Fullscreen update received, remote session {0}", RemoteSession.Id); FullscreenEventPending = false; // screenshot request if (ScreenshotEventLock != null) { lock (ScreenshotEventLock) { // screenshot image index ScreenshotImageIdx = image.Idx; // if waiting for a screenshot, signal the reception if (ScreenshotEventPending) { ScreenshotEventPending = false; Monitor.Pulse(ScreenshotEventLock); } } } } // send update to client(s) foreach (var client in Clients.Values) { // send the update if the client latency is normal or if it's a fullscreen update if (client.Latency <= _imageCacheDuration || image.Fullscreen) { // websocket(s) if (client.WebSockets != null && client.WebSockets.Count > 0) { // round robin on the client websockets down pool if (client.WebSocketsRoundRobinIdx >= client.WebSockets.Count) { client.WebSocketsRoundRobinIdx = 0; } Trace.TraceInformation("Sending image {0} ({1}) on websocket, client {2}, remote session {3}", image.Idx, (image.Fullscreen ? "screen" : "region"), client.Id, RemoteSession.Id); client.WebSockets[client.WebSocketsRoundRobinIdx++].SendImage(image); } // event source else if (client.EventSource != null) { Trace.TraceInformation("Sending image {0} ({1}) on event source, client {2}, remote session {3}", image.Idx, (image.Fullscreen ? "screen" : "region"), client.Id, RemoteSession.Id); client.EventSource.SendImage(image); } // long polling else if (client.LongPolling != null) { Trace.TraceInformation("Sending image {0} ({1}) on long polling, client {2}, remote session {3}", image.Idx, (image.Fullscreen ? "screen" : "region"), client.Id, RemoteSession.Id); client.LongPolling.SendImage(image); } // xhr: updates are polled against the cache by the client } } // image event lock (_imageEventLock) { // last received image index _lastReceivedImageIdx = image.Idx; // if waiting for a new image, signal the reception if (_imageEventPending) { _imageEventPending = false; Monitor.Pulse(_imageEventLock); } } } catch (Exception exc) { Trace.TraceError("Failed to process update, remote session {0} ({1})", RemoteSession.Id, exc); } }
public void SendImage(RemoteSessionImage image) { Send(GetImageText(image)); }
public void SendImage(RemoteSessionImage image) { Send("<script>parent.lpProcessImage(" + GetImageText(image) + ");</script>"); }
public void SendImage(RemoteSessionImage image) { Send(string.Format("<script>parent.lpProcessImage({0});</script>", GetImageText(image))); }