diff --git a/Server/Controls/PictureBoxEx.Designer.cs b/Server/Controls/PictureBoxEx.Designer.cs index 87e5bf7c..bc884415 100644 --- a/Server/Controls/PictureBoxEx.Designer.cs +++ b/Server/Controls/PictureBoxEx.Designer.cs @@ -16,7 +16,11 @@ protected override void Dispose(bool disposing) if (disposing && (components != null)) { components.Dispose(); + + // Stop running. + this.Stop(); } + base.Dispose(disposing); } diff --git a/Server/Controls/PictureBoxEx.cs b/Server/Controls/PictureBoxEx.cs index 52e50e93..09bfcecc 100644 --- a/Server/Controls/PictureBoxEx.cs +++ b/Server/Controls/PictureBoxEx.cs @@ -1,21 +1,129 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; +using System.Diagnostics; using System.Drawing; -using System.Linq; -using System.Text; using System.Windows.Forms; +using xServer.Core.Utilities; namespace xServer.Controls { - public partial class PictureBoxEx : PictureBox + public delegate void PictureSizeChangedEventHandler(int width, int height); + + public class PictureSizeChangedEventArgs : EventArgs { + public int NewWidth; + public int NewHeight; + + public PictureSizeChangedEventArgs(int width, int height) + { + NewWidth = width; + NewHeight = height; + } + } + + public interface IRapidPictureBox + { + bool Running { get; set; } + + void Start(); + void Stop(); + void UpdateImage(Bitmap bmp, bool cloneBitmap = false); + + Image _Image { get; set; } + } + + /// + /// Custom PictureBox Control designed for rapidly-changing images. + /// + public partial class PictureBoxEx : PictureBox, IRapidPictureBox + { + #region IRapidPictureBox Implementation + + public bool Running { get; set; } + + public void Start() + { + _frameCounter = new FrameCounter(); + + _sWatch = Stopwatch.StartNew(); + + Running = true; + } + + public void Stop() + { + if (_sWatch != null) + _sWatch.Stop(); + + Running = false; + } + + public void UpdateImage(Bitmap bmp, bool cloneBitmap = false) + { + try + { + CountFps(); + + if ((bmpWidth != bmp.Width) && (bmpHeight != bmp.Height)) + OnPictureSizeChanged(new PictureSizeChangedEventArgs(bmp.Width, bmp.Height)); + + lock (ImgLocker) + { + if (this._Image != null) + { + this._Image.Dispose(); + this._Image = null; + } + + this._Image = cloneBitmap ? new Bitmap(bmp, picDesktop.Width, picDesktop.Height) /*resize bitmap*/ : bmp; + } + } + catch (InvalidOperationException) + { + } + catch (Exception ex) + { + MessageBox.Show( + string.Format( + "An unexpected error occurred: {0}\n\nPlease report this as fast as possible here:\\https://github.com/MaxXor/xRAT/issues", + ex.Message), "", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + #endregion + + // Fields used to keep track of the remote desktop's size. + public int bmpWidth { get; private set; } + public int bmpHeight { get; private set; } + + // Fields for the FrameCounter. + public FrameCounter _frameCounter; + private Stopwatch _sWatch; + public PictureBoxEx() { InitializeComponent(); + + //this.DoubleBuffered = true; + this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); + + _frameCounter = new FrameCounter(); } - + + #region Events + + public event PictureSizeChangedEventHandler PictureSizeChanged; + + protected virtual void OnPictureSizeChanged(PictureSizeChangedEventArgs e) + { + PictureSizeChangedEventHandler handler = PictureSizeChanged; + if (handler != null) + handler(e.NewWidth, e.NewHeight); + } + + #endregion + + #region Overrides + protected override CreateParams CreateParams { get @@ -28,7 +136,42 @@ protected override CreateParams CreateParams protected override void OnPaint(PaintEventArgs pe) { - base.OnPaint(pe); + lock (ImgLocker) + { + if (this._Image != null) + { + pe.Graphics.DrawImage(this._Image, this.Location); + } + } + } + + #endregion + + private void CountFps() + { + var deltaTime = (float)_sWatch.Elapsed.TotalSeconds; + _sWatch = Stopwatch.StartNew(); + + _frameCounter.Update(deltaTime); + } + + private readonly object ImgLocker = new object(); + /// + /// Provides thread-safe access to the PictureBox's image. + /// + public Image _Image + { + get + { + return picDesktop.Image; + } + set + { + lock (ImgLocker) + { + picDesktop.Image = value; + } + } } } } \ No newline at end of file diff --git a/Server/Core/Utilities/FrameCounter.cs b/Server/Core/Utilities/FrameCounter.cs index 541c5b45..0b13b294 100644 --- a/Server/Core/Utilities/FrameCounter.cs +++ b/Server/Core/Utilities/FrameCounter.cs @@ -1,22 +1,36 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace xServer.Core.Utilities { + public class FrameUpdatedEventArgs : EventArgs + { + public float CurrentFramesPerSecond { get; private set; } + + public FrameUpdatedEventArgs(float _CurrentFramesPerSecond) + { + CurrentFramesPerSecond = _CurrentFramesPerSecond; + } + } + + public delegate void FrameUpdatedEventHandler(FrameUpdatedEventArgs e); + public class FrameCounter { public long TotalFrames { get; private set; } public float TotalSeconds { get; private set; } public float AverageFramesPerSecond { get; private set; } - public float CurrentFramesPerSecond { get; private set; } public const int MAXIMUM_SAMPLES = 100; private Queue _sampleBuffer = new Queue(); + public event FrameUpdatedEventHandler FrameUpdated; + public void Update(float deltaTime) { - CurrentFramesPerSecond = 1.0f / deltaTime; + float CurrentFramesPerSecond = 1.0f / deltaTime; _sampleBuffer.Enqueue(CurrentFramesPerSecond); @@ -30,8 +44,17 @@ public void Update(float deltaTime) AverageFramesPerSecond = CurrentFramesPerSecond; } + OnFrameUpdated(new FrameUpdatedEventArgs(AverageFramesPerSecond)); + TotalFrames++; TotalSeconds += deltaTime; } + + protected virtual void OnFrameUpdated(FrameUpdatedEventArgs e) + { + FrameUpdatedEventHandler handler = FrameUpdated; + if (handler != null) + handler(e); + } } -} +} \ No newline at end of file diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index 7bed0fb3..1afe51e0 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.IO; using System.Windows.Forms; @@ -17,8 +16,7 @@ public partial class FrmRemoteDesktop : Form private readonly Client _connectClient; private bool _enableMouseInput; private bool _started; - private FrameCounter _frameCounter; - private Stopwatch _sWatch; + private int _screenWidth; private int _screenHeight; @@ -31,18 +29,20 @@ public FrmRemoteDesktop(Client c) _connectClient = c; _connectClient.Value.FrmRdp = this; InitializeComponent(); + + picDesktop.PictureSizeChanged += picDesktop_PictureSizeChanged; } private void FrmRemoteDesktop_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("Remote Desktop", _connectClient); - panelTop.Left = (this.Width/2) - (panelTop.Width/2); + panelTop.Left = (this.Width / 2) - (panelTop.Width / 2); - btnHide.Left = (panelTop.Width/2) - (btnHide.Width/2); + btnHide.Left = (panelTop.Width / 2) - (btnHide.Width / 2); btnShow.Location = new Point(377, 0); - btnShow.Left = (this.Width/2) - (btnShow.Width/2); + btnShow.Left = (this.Width / 2) - (btnShow.Width / 2); if (_connectClient.Value != null) new Core.Packets.ServerPackets.GetMonitors().Execute(_connectClient); @@ -50,7 +50,7 @@ private void FrmRemoteDesktop_Load(object sender, EventArgs e) public void ProcessScreens(object state) { - while (true) + while (true && picDesktop != null && !picDesktop.IsDisposed && !picDesktop.Disposing) { GetDesktopResponse packet; lock (ProcessingScreensQueue) @@ -68,21 +68,36 @@ public void ProcessScreens(object state) } if (_connectClient.Value.StreamCodec == null) - _connectClient.Value.StreamCodec = new UnsafeStreamCodec(packet.Quality, packet.Monitor, packet.Resolution); - - if (_connectClient.Value.StreamCodec.ImageQuality != packet.Quality || _connectClient.Value.StreamCodec.Monitor != packet.Monitor - || _connectClient.Value.StreamCodec.Resolution != packet.Resolution) { - if (_connectClient.Value.StreamCodec != null) + _connectClient.Value.StreamCodec = new UnsafeStreamCodec(packet.Quality, packet.Monitor, packet.Resolution); + } + else if (_connectClient.Value.StreamCodec.ImageQuality != packet.Quality || _connectClient.Value.StreamCodec.Monitor != packet.Monitor) + { + if (string.Compare(_connectClient.Value.StreamCodec.Resolution, packet.Resolution, StringComparison.InvariantCultureIgnoreCase) != 0) + { _connectClient.Value.StreamCodec.Dispose(); + } _connectClient.Value.StreamCodec = new UnsafeStreamCodec(packet.Quality, packet.Monitor, packet.Resolution); } using (MemoryStream ms = new MemoryStream(packet.Image)) { - if (_connectClient.Value.FrmRdp != null) - _connectClient.Value.FrmRdp.UpdateImage(_connectClient.Value.StreamCodec.DecodeData(ms), true); + try + { + // Update the new image from the packet data. + picDesktop.UpdateImage(_connectClient.Value.StreamCodec.DecodeData(ms), true); + + this.Invoke((MethodInvoker)delegate + { + if (picDesktop != null && !picDesktop.IsDisposed && picDesktop._Image != null) + { + picDesktop.Invalidate(); + } + }); + } + catch + { } } packet.Image = null; @@ -93,7 +108,7 @@ public void AddMonitors(int montiors) { try { - cbMonitors.Invoke((MethodInvoker) delegate + cbMonitors.Invoke((MethodInvoker)delegate { for (int i = 0; i < montiors; i++) cbMonitors.Items.Add(string.Format("Monitor {0}", i + 1)); @@ -112,73 +127,27 @@ public void AddMonitors(int montiors) } } - private void CountFps() + // Update on frame change. + private void _frameCounter_FrameUpdated(FrameUpdatedEventArgs e) { - var deltaTime = (float)_sWatch.Elapsed.TotalSeconds; - _sWatch = Stopwatch.StartNew(); - - _frameCounter.Update(deltaTime); - - UpdateFps(_frameCounter.AverageFramesPerSecond); + this.Invoke((MethodInvoker)delegate + { + this.Text = string.Format("{0} - FPS: {1}", WindowHelper.GetWindowTitle("Remote Desktop", _connectClient), e.CurrentFramesPerSecond.ToString("0.00")); + }); } - private void UpdateFps(float fps) - { - try - { - this.Invoke((MethodInvoker)delegate - { - this.Text = string.Format("{0} - FPS: {1}", WindowHelper.GetWindowTitle("Remote Desktop", _connectClient), fps.ToString("0.00")); - }); - } - catch (InvalidOperationException) - { - } - } - - private void UpdateScreenResolution(int width, int height) + private void picDesktop_PictureSizeChanged(int width, int height) { _screenWidth = width; _screenHeight = height; } - public void UpdateImage(Bitmap bmp, bool cloneBitmap = false) - { - try - { - CountFps(); - UpdateScreenResolution(bmp.Width, bmp.Height); - picDesktop.Invoke((MethodInvoker) delegate - { - // get old image to dispose it correctly - var oldImage = picDesktop.Image; - - picDesktop.SuspendLayout(); - picDesktop.Image = cloneBitmap ? new Bitmap(bmp, picDesktop.Width, picDesktop.Height) /*resize bitmap*/ : bmp; - picDesktop.ResumeLayout(); - - if (oldImage != null) - oldImage.Dispose(); - }); - } - catch (InvalidOperationException) - { - } - catch (Exception ex) - { - MessageBox.Show( - string.Format( - "An unexpected error occurred: {0}\n\nPlease report this as fast as possible here:\\https://github.com/MaxXor/xRAT/issues", - ex.Message), "", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - private void ToggleControls(bool t) { _started = !t; try { - this.Invoke((MethodInvoker) delegate + this.Invoke((MethodInvoker)delegate { btnStart.Enabled = t; btnStop.Enabled = !t; @@ -194,16 +163,16 @@ private void FrmRemoteDesktop_FormClosing(object sender, FormClosingEventArgs e) { if (_started) new Core.Packets.ServerPackets.GetDesktop(0, 0, RemoteDesktopAction.Stop).Execute(_connectClient); - if (_sWatch != null) - _sWatch.Stop(); + if (!picDesktop.IsDisposed && !picDesktop.Disposing) + picDesktop.Dispose(); if (_connectClient.Value != null) _connectClient.Value.FrmRdp = null; } private void FrmRemoteDesktop_Resize(object sender, EventArgs e) { - panelTop.Left = (this.Width/2) - (panelTop.Width/2); - btnShow.Left = (this.Width/2) - (btnShow.Width/2); + panelTop.Left = (this.Width / 2) - (panelTop.Width / 2); + btnShow.Left = (this.Width / 2) - (btnShow.Width / 2); } private void btnStart_Click(object sender, EventArgs e) @@ -215,11 +184,13 @@ private void btnStart_Click(object sender, EventArgs e) return; } - _frameCounter = new FrameCounter(); - _sWatch = Stopwatch.StartNew(); - ToggleControls(false); + picDesktop.Start(); + + // Subscribe to the new frame counter. + picDesktop._frameCounter.FrameUpdated += _frameCounter_FrameUpdated; + new Core.Packets.ServerPackets.GetDesktop(barQuality.Value, cbMonitors.SelectedIndex, RemoteDesktopAction.Start).Execute(_connectClient); } @@ -227,7 +198,11 @@ private void btnStop_Click(object sender, EventArgs e) { new Core.Packets.ServerPackets.GetDesktop(0, 0, RemoteDesktopAction.Stop).Execute(_connectClient); ToggleControls(true); - _sWatch.Stop(); + + picDesktop.Stop(); + + // Unsubscribe from the frame counter. It will be re-created when starting again. + picDesktop._frameCounter.FrameUpdated -= _frameCounter_FrameUpdated; } private void barQuality_Scroll(object sender, EventArgs e)