From 4f5f36903b3b8ee7bd9513a1a2662e9bf852d457 Mon Sep 17 00:00:00 2001 From: Jean-Pierre LESUEUR Date: Thu, 20 Jan 2022 16:48:19 +0100 Subject: [PATCH 1/8] Fix: can't connect back to server when idle bug fix --- .../PowerRemoteDesktop_Server.psm1 | 30 ++++++++++++------- TestViewer.ps1 | 4 +-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 index d53614f..a1cb020 100644 --- a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 +++ b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 @@ -57,6 +57,7 @@ $global:PowerRemoteDesktopVersion = "1.0.5.beta.6" $global:HostSyncHash = [HashTable]::Synchronized(@{ host = $host ClipboardText = (Get-Clipboard -Raw) + RunningSession = $false }) enum TransportMode { @@ -1130,10 +1131,10 @@ $global:DesktopStreamScriptBlock = { $encoderParameters = New-Object System.Drawing.Imaging.EncoderParameters(1) $encoderParameters.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::Quality, $imageQuality) - $packetSize = 4096 - - while ($true) - { + $packetSize = 4096 + + while ($global:HostSyncHash.RunningSession) + { try { $desktopImage = Get-DesktopImage -Screen $Param.Screen @@ -1323,7 +1324,7 @@ $global:IngressEventScriptBlock = { $keyboardSim = [KeyboardSim]::New() - while ($true) + while ($global:HostSyncHash.RunningSession) { try { @@ -1626,7 +1627,7 @@ $global:EgressEventScriptBlock = { $stopWatch = [System.Diagnostics.Stopwatch]::StartNew() - while ($true) + while ($global:HostSyncHash.RunningSession) { # Events that occurs every seconds needs to be placed bellow. # If no event has occured during this second we send a Keep-Alive signal to @@ -1929,6 +1930,8 @@ function Invoke-RemoteDesktopServer { try { + $global:HostSyncHash.RunningSession = $false + Write-Verbose "Server waiting for new incomming session..." # Establish a new Remote Desktop Session. @@ -1939,7 +1942,9 @@ function Invoke-RemoteDesktopServer # Otherwise a Timeout Exception will be raised. # Actually, if someone else decide to connect in the mean time it will interrupt the whole session, # Remote Viewer will then need to establish a new session from scratch. - $clientEvents = $server.PullClient(10 * 1000); + $clientEvents = $server.PullClient(10 * 1000); + + $global:HostSyncHash.RunningSession = $true # Grab desired screen to capture $screen = [System.Windows.Forms.Screen]::AllScreens | Where-Object -FilterScript { $_.DeviceName -eq $server.Session.Screen } @@ -1983,16 +1988,19 @@ function Invoke-RemoteDesktopServer # Waiting for Runspaces to finish their jobs. while ($true) { - $completed = $true + $completed = $true # Probe each existing runspaces foreach ($runspace in $runspaces) { if (-not $runspace.AsyncResult.IsCompleted) { - $completed = $false - - break + $completed = $false + } + elseif ($global:HostSyncHash.RunningSession) + { + # Notifying other runspaces that a session integrity was lost + $global:HostSyncHash.RunningSession = $false } } diff --git a/TestViewer.ps1 b/TestViewer.ps1 index 956f593..588a98c 100644 --- a/TestViewer.ps1 +++ b/TestViewer.ps1 @@ -2,5 +2,5 @@ Write-Output "This script is used during development phase. Never run this scrip Invoke-Expression -Command (Get-Content "PowerRemoteDesktop_Viewer\PowerRemoteDesktop_Viewer.psm1" -Raw) -Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "127.0.0.1" -#Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "172.31.115.183" \ No newline at end of file +#Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "127.0.0.1" +Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "172.31.115.183" \ No newline at end of file From 7480795b7a9653235714fa5624c8ab50a6fe5f5b Mon Sep 17 00:00:00 2001 From: Jean-Pierre LESUEUR Date: Thu, 20 Jan 2022 16:51:34 +0100 Subject: [PATCH 2/8] Fix: mute errors on GUI when we lost connectivity with server. --- .../PowerRemoteDesktop_Viewer.psm1 | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 index fa051eb..c6e0680 100644 --- a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 +++ b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 @@ -1650,7 +1650,12 @@ function Invoke-RemoteDesktopViewer $aEvent = (New-MouseEvent -X $X -Y $Y -Button $Button -Type $Type) - $outputEventSyncHash.Writer.WriteLine(($aEvent | ConvertTo-Json -Compress)) + try + { + $outputEventSyncHash.Writer.WriteLine(($aEvent | ConvertTo-Json -Compress)) + } + catch + {} } function Send-VirtualKeyboard @@ -1673,7 +1678,12 @@ function Invoke-RemoteDesktopViewer $aEvent = (New-KeyboardEvent -Keys $KeyChain) - $outputEventSyncHash.Writer.WriteLine(($aEvent | ConvertTo-Json -Compress)) + try + { + $outputEventSyncHash.Writer.WriteLine(($aEvent | ConvertTo-Json -Compress)) + } + catch + {} } $virtualDesktopSyncHash.VirtualDesktop.Form.Add_KeyPress( @@ -1775,7 +1785,11 @@ function Invoke-RemoteDesktopViewer Delta = $_.Delta } - $outputEventSyncHash.Writer.WriteLine(($aEvent | ConvertTo-Json -Compress)) + try + { + $outputEventSyncHash.Writer.WriteLine(($aEvent | ConvertTo-Json -Compress)) + } + catch {} } ) } From 35627553169b6056d1b8214dcf9c6e0bc2b0eb01 Mon Sep 17 00:00:00 2001 From: Jean-Pierre LESUEUR Date: Thu, 20 Jan 2022 17:04:46 +0100 Subject: [PATCH 3/8] TransportMode option removed, desktop images are sent in raw systematically (better performance) --- .../PowerRemoteDesktop_Server.psm1 | 100 +++++------------- .../PowerRemoteDesktop_Viewer.psm1 | 77 +++++--------- README.md | 5 +- TestViewer.ps1 | 4 +- 4 files changed, 55 insertions(+), 131 deletions(-) diff --git a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 index a1cb020..1a34654 100644 --- a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 +++ b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 @@ -60,11 +60,6 @@ $global:HostSyncHash = [HashTable]::Synchronized(@{ RunningSession = $false }) -enum TransportMode { - Raw = 1 - Base64 = 2 -} - enum ClipboardMode { Disabled = 1 Receive = 2 @@ -601,14 +596,12 @@ class ClientIO { [System.IO.StreamWriter] $Writer = $null [System.IO.StreamReader] $Reader = $null [System.Net.Security.SslStream] $SSLStream = $null - [TransportMode] $TransportMode ClientIO( [System.Net.Sockets.TcpClient] $Client, [System.Security.Cryptography.X509Certificates.X509Certificate2] $Certificate, - [bool] $TLSv1_3, - [TransportMode] $TransportMode + [bool] $TLSv1_3 ) { <# .SYNOPSIS @@ -622,9 +615,6 @@ class ClientIO { .PARAMETER TLSv1_3 Define whether or not SSL/TLS v1.3 must be used. - - .PARAMETER TransportMode - Define transport method for streams (Base64 or Raw) #> if ((-not $Client) -or (-not $Certificate)) @@ -633,7 +623,6 @@ class ClientIO { } $this.Client = $Client - $this.TransportMode = $TransportMode Write-Verbose "Create new SSL Stream..." @@ -784,7 +773,6 @@ class ClientIO { $sessionInformation = Get-LocalMachineInformation - $sessionInformation | Add-Member -MemberType NoteProperty -Name "TransportMode" -Value $this.TransportMode $sessionInformation | Add-Member -MemberType NoteProperty -Name "SessionId" -Value $session.Id $sessionInformation | Add-Member -MemberType NoteProperty -Name "Version" -Value $global:PowerRemoteDesktopVersion $sessionInformation | Add-Member -MemberType NoteProperty -Name "ViewOnly" -Value $ViewOnly @@ -855,7 +843,6 @@ class ServerIO { [string] $ListenAddress = "127.0.0.1" [int] $ListenPort = 2801 [bool] $TLSv1_3 = $false - [TransportMode] $TransportMode [string] $Password [bool] $ViewOnly = $false @@ -889,9 +876,6 @@ class ServerIO { .PARAMETER TLSv1_3 Define if TLS v1.3 must be used. - .PARAMETER TransportMode - Define stream transport method. - .PARAMETER ViewOnly Define if mouse / keyboard is authorized. #> @@ -899,7 +883,6 @@ class ServerIO { [string] $ListenAddress, [int] $ListenPort, [string] $Password, - [TransportMode] $TransportMode, [System.Security.Cryptography.X509Certificates.X509Certificate2] $Certificate, [bool] $TLSv1_3, [bool] $ViewOnly @@ -914,7 +897,6 @@ class ServerIO { $this.ListenPort = $ListenPort $this.TLSv1_3 = $TLSv1_3 $this.Password = $Password - $this.TransportMode = $TransportMode $this.ViewOnly = $ViewOnly if (-not $Certificate) @@ -998,8 +980,7 @@ class ServerIO { $client = [ClientIO]::New( $socket, $this.Certificate, - $this.TLSv1_3, - $this.TransportMode + $this.TLSv1_3 ) try { @@ -1060,11 +1041,6 @@ class ServerIO { $global:DesktopStreamScriptBlock = { - enum TransportMode { - Raw = 1 - Base64 = 2 - } - function Get-DesktopImage { <# .SYNOPSIS @@ -1164,50 +1140,33 @@ $global:DesktopStreamScriptBlock = { { $imageStream.position = 0 try - { - switch ([TransportMode] $Param.Client.TransportMode) - { - ([TransportMode]::Raw) - { - $Param.Client.SSLStream.Write([BitConverter]::GetBytes([int32] $imageStream.Length) , 0, 4) # SizeOf(Int32) - - $totalBytesSent = 0 - - $buffer = New-Object -TypeName byte[] -ArgumentList $packetSize - do - { - $bufferSize = ($imageStream.Length - $totalBytesSent) - if ($bufferSize -gt $packetSize) - { - $bufferSize = $packetSize - } - else - { - # Save some memory operations for creating objects. - # Usually, bellow code is call when last chunk is being sent. - $buffer = New-Object -TypeName byte[] -ArgumentList $bufferSize - } - - # (OPTIMIZATION IDEA): Try with BinaryStream to save the need of "byte[]"" buffer. - $null = $imageStream.Read($buffer, 0, $buffer.Length) - - $Param.Client.SSLStream.Write($buffer, 0, $buffer.Length) - - $totalBytesSent += $bufferSize - } until ($totalBytesSent -eq $imageStream.Length) + { + $Param.Client.SSLStream.Write([BitConverter]::GetBytes([int32] $imageStream.Length) , 0, 4) # SizeOf(Int32) - break - } + $totalBytesSent = 0 + + $buffer = New-Object -TypeName byte[] -ArgumentList $packetSize + do + { + $bufferSize = ($imageStream.Length - $totalBytesSent) + if ($bufferSize -gt $packetSize) + { + $bufferSize = $packetSize + } + else + { + # Save some memory operations for creating objects. + # Usually, bellow code is call when last chunk is being sent. + $buffer = New-Object -TypeName byte[] -ArgumentList $bufferSize + } - ([TransportMode]::Base64) - { - $Param.Client.Writer.WriteLine( - [System.Convert]::ToBase64String($imageStream.ToArray()) - ) + # (OPTIMIZATION IDEA): Try with BinaryStream to save the need of "byte[]"" buffer. + $null = $imageStream.Read($buffer, 0, $buffer.Length) - break - } - } + $Param.Client.SSLStream.Write($buffer, 0, $buffer.Length) + + $totalBytesSent += $bufferSize + } until ($totalBytesSent -eq $imageStream.Length) } catch { break } @@ -1793,10 +1752,6 @@ function Invoke-RemoteDesktopServer .PARAMETER EncodedCertificate A valid X509 Certificate (With Private Key) encoded as a Base64 String. - .PARAMETER TransportMode - Tell server how to send desktop image to remote viewer. Best method is Raw Bytes but I decided to keep - the Base64 transport method as an alternative. - .PARAMETER TLSv1_3 Define whether or not TLS v1.3 must be used for communication with Viewer. @@ -1829,7 +1784,6 @@ function Invoke-RemoteDesktopServer # Or [string] $EncodedCertificate = "", # 2 - [TransportMode] $TransportMode = [TransportMode]::Raw, [switch] $TLSv1_3, [switch] $DisableVerbosity, [int] $ImageQuality = 100, @@ -1912,13 +1866,11 @@ function Invoke-RemoteDesktopServer } } - Write-Verbose $TransportMode # Create new server and listen $server = [ServerIO]::New( $ListenAddress, $ListenPort, $Password, - $TransportMode, $Certificate, $TLSv1_3, $ViewOnly diff --git a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 index c6e0680..5d5a71d 100644 --- a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 +++ b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 @@ -72,11 +72,6 @@ enum ClipboardMode { Both = 4 } -enum TransportMode { - Raw = 1 - Base64 = 2 -} - function Write-Banner { <# @@ -616,7 +611,6 @@ class ClientIO { (-not ($sessionInformation.PSobject.Properties.name -contains "Username")) -or (-not ($sessionInformation.PSobject.Properties.name -contains "WindowsVersion")) -or (-not ($sessionInformation.PSobject.Properties.name -contains "SessionId")) -or - (-not ($sessionInformation.PSobject.Properties.name -contains "TransportMode")) -or (-not ($sessionInformation.PSobject.Properties.name -contains "Version")) -or (-not ($sessionInformation.PSobject.Properties.name -contains "Screens")) -or (-not ($sessionInformation.PSobject.Properties.name -contains "ViewOnly")) @@ -879,11 +873,6 @@ class ViewerSession $global:VirtualDesktopUpdaterScriptBlock = { - enum TransportMode { - Raw = 1 - Base64 = 2 - } - function Invoke-SmoothResize { <# @@ -949,53 +938,40 @@ $global:VirtualDesktopUpdaterScriptBlock = { { $stream = New-Object System.IO.MemoryStream try - { - switch ([TransportMode] $Param.TransportMode) - { - ([TransportMode]::Raw) - { - $buffer = New-Object -TypeName byte[] -ArgumentList 4 # SizeOf(Int32) - - $Param.Client.SSLStream.Read($buffer, 0, $buffer.Length) + { + $buffer = New-Object -TypeName byte[] -ArgumentList 4 # SizeOf(Int32) - [int32] $totalBufferSize = [BitConverter]::ToInt32($buffer, 0) + $Param.Client.SSLStream.Read($buffer, 0, $buffer.Length) - $stream.SetLength($totalBufferSize) + [int32] $totalBufferSize = [BitConverter]::ToInt32($buffer, 0) - $stream.position = 0 + $stream.SetLength($totalBufferSize) - $totalBytesRead = 0 - - $buffer = New-Object -TypeName Byte[] -ArgumentList $packetSize - do - { - $bufferSize = $totalBufferSize - $totalBytesRead - if ($bufferSize -gt $packetSize) - { - $bufferSize = $packetSize - } - else - { - # Save some memory operations for creating objects. - # Usually, bellow code is call when last chunk is being sent. - $buffer = New-Object -TypeName byte[] -ArgumentList $bufferSize - } + $stream.position = 0 - $Param.Client.SSLStream.Read($buffer, 0, $bufferSize) + $totalBytesRead = 0 - $null = $stream.Write($buffer, 0, $buffer.Length) + $buffer = New-Object -TypeName Byte[] -ArgumentList $packetSize + do + { + $bufferSize = $totalBufferSize - $totalBytesRead + if ($bufferSize -gt $packetSize) + { + $bufferSize = $packetSize + } + else + { + # Save some memory operations for creating objects. + # Usually, bellow code is call when last chunk is being sent. + $buffer = New-Object -TypeName byte[] -ArgumentList $bufferSize + } - $totalBytesRead += $bufferSize - } until ($totalBytesRead -eq $totalBufferSize) - } + $Param.Client.SSLStream.Read($buffer, 0, $bufferSize) - ([TransportMode]::Base64) - { - [byte[]] $buffer = [System.Convert]::FromBase64String(($Param.Client.Reader.ReadLine())) + $null = $stream.Write($buffer, 0, $buffer.Length) - $stream.Write($buffer, 0, $buffer.Length) - } - } + $totalBytesRead += $bufferSize + } until ($totalBytesRead -eq $totalBufferSize) $stream.Position = 0 @@ -1801,8 +1777,7 @@ function Invoke-RemoteDesktopViewer VirtualDesktopSyncHash = $virtualDesktopSyncHash VirtualDesktopWidth = $virtualDesktopWidth VirtualDesktopHeight = $virtualDesktopHeight - RequireResize = $requireResize - TransportMode = [TransportMode] $session.SessionInformation.TransportMode + RequireResize = $requireResize } $newRunspace = (New-RunSpace -ScriptBlock $global:VirtualDesktopUpdaterScriptBlock -Param $param) diff --git a/README.md b/README.md index 7c14bf3..652222c 100644 --- a/README.md +++ b/README.md @@ -192,9 +192,6 @@ Supported options: * `Password` (**Mandatory**): Define password used during authentication process. * `CertificateFile` (Default: **None**): A valid X509 Certificate (With Private Key) File. If set, this parameter is prioritize. * `EncodedCertificate` (Default: **None**): A valid X509 Certificate (With Private Key) encoded as a Base64 String. -* `TransportMode`(Default: `Raw`): Define which method to use to transfer streams. - * `Raw`: Transfer streams as raw bytes (recommended) - * `Base64`: Transfer streams as base64 encoded string * `TLSv1_3` (Default: None): If this switch is present, server will use TLS v1.3 instead of TLS v1.2. Use this option only if both viewer and server support TLS v1.3. * `DisableVerbosity` (Default: None): If this switch is present, verbosity will be hidden from console. * `ImageQuality` (Default: `100`): JPEG Compression level from 0 to 100. 0 = Lowest quality, 100 = Highest quality. @@ -267,7 +264,7 @@ Detail Fingerprint ### 11 January 2022 (1.0.1 Beta 2) -* Desktop images are now transported in raw bytes instead of base64 string thus slightly improving performances. Base64 Transport Method is still available through an option but disabled by default. +* Desktop images are now transported in raw bytes instead of base64 string thus slightly improving performances. * Protocol has drastically changed. It is smoother to read and less prone to errors. * TLS v1.3 option added (Might not be supported by some systems). * Several code optimization, refactoring and fixes. diff --git a/TestViewer.ps1 b/TestViewer.ps1 index 588a98c..956f593 100644 --- a/TestViewer.ps1 +++ b/TestViewer.ps1 @@ -2,5 +2,5 @@ Write-Output "This script is used during development phase. Never run this scrip Invoke-Expression -Command (Get-Content "PowerRemoteDesktop_Viewer\PowerRemoteDesktop_Viewer.psm1" -Raw) -#Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "127.0.0.1" -Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "172.31.115.183" \ No newline at end of file +Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "127.0.0.1" +#Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "172.31.115.183" \ No newline at end of file From 02c781d4b95e732d2681e3948107d5cfb8daf936 Mon Sep 17 00:00:00 2001 From: Jean-Pierre LESUEUR Date: Fri, 21 Jan 2022 10:54:13 +0100 Subject: [PATCH 4/8] Perf: multiple code improvements to speed desktop image transmission --- .../PowerRemoteDesktop_Server.psm1 | 27 +++----- .../PowerRemoteDesktop_Viewer.psm1 | 62 ++++++++++--------- 2 files changed, 39 insertions(+), 50 deletions(-) diff --git a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 index 1a34654..d269835 100644 --- a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 +++ b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 @@ -1107,7 +1107,7 @@ $global:DesktopStreamScriptBlock = { $encoderParameters = New-Object System.Drawing.Imaging.EncoderParameters(1) $encoderParameters.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::Quality, $imageQuality) - $packetSize = 4096 + $packetSize = 9216 # 9KiB while ($global:HostSyncHash.RunningSession) { @@ -1142,31 +1142,18 @@ $global:DesktopStreamScriptBlock = { try { $Param.Client.SSLStream.Write([BitConverter]::GetBytes([int32] $imageStream.Length) , 0, 4) # SizeOf(Int32) - - $totalBytesSent = 0 - - $buffer = New-Object -TypeName byte[] -ArgumentList $packetSize + + $binaryReader = New-Object System.IO.BinaryReader($imageStream) do { - $bufferSize = ($imageStream.Length - $totalBytesSent) + $bufferSize = ($imageStream.Length - $imageStream.Position) if ($bufferSize -gt $packetSize) { $bufferSize = $packetSize - } - else - { - # Save some memory operations for creating objects. - # Usually, bellow code is call when last chunk is being sent. - $buffer = New-Object -TypeName byte[] -ArgumentList $bufferSize - } - - # (OPTIMIZATION IDEA): Try with BinaryStream to save the need of "byte[]"" buffer. - $null = $imageStream.Read($buffer, 0, $buffer.Length) - - $Param.Client.SSLStream.Write($buffer, 0, $buffer.Length) + } - $totalBytesSent += $bufferSize - } until ($totalBytesSent -eq $imageStream.Length) + $Param.Client.SSLStream.Write($binaryReader.ReadBytes($bufferSize), 0, $bufferSize) + } until ($imageStream.Position -eq $imageStream.Length) } catch { break } diff --git a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 index 5d5a71d..b8356f5 100644 --- a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 +++ b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 @@ -361,6 +361,7 @@ class ClientIO { [System.Net.Security.SslStream] $SSLStream = $null [System.IO.StreamWriter] $Writer = $null [System.IO.StreamReader] $Reader = $null + [System.IO.BinaryReader] $BinaryReader = $null ClientIO( <# @@ -519,6 +520,8 @@ class ClientIO { $this.Reader = New-Object System.IO.StreamReader($this.SSLStream) + $this.BinaryReader = New-Object System.IO.BinaryReader($this.SSLStream) + Write-Verbose "Encrypted tunnel opened and ready for use." } @@ -725,6 +728,11 @@ class ClientIO { $this.Reader.Close() } + if ($this.BinaryReader) + { + $this.BinaryReader.Close() + } + if ($this.SSLStream) { $this.SSLStream.Close() @@ -888,6 +896,9 @@ $global:VirtualDesktopUpdaterScriptBlock = { .PARAMETER NewHeight Define the height of new bitmap version. + .PARAMETER HighQuality + Activate high quality image resizing with a serious performance cost. + .EXAMPLE Invoke-SmoothResize -OriginalImage $myImage -NewWidth 1920 -NewHeight 1024 #> @@ -899,7 +910,9 @@ $global:VirtualDesktopUpdaterScriptBlock = { [int] $NewWidth, [Parameter(Mandatory=$true)] - [int] $NewHeight + [int] $NewHeight, + + [bool] $HighQuality = $false ) try { @@ -907,10 +920,13 @@ $global:VirtualDesktopUpdaterScriptBlock = { $resizedImage = [System.Drawing.Graphics]::FromImage($bitmap) - $resizedImage.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality - $resizedImage.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality - $resizedImage.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic - $resizedImage.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality + if ($HighQuality) + { + $resizedImage.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality + $resizedImage.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality + $resizedImage.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic + $resizedImage.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality + } $resizedImage.DrawImage($OriginalImage, 0, 0, $bitmap.Width, $bitmap.Height) @@ -932,10 +948,10 @@ $global:VirtualDesktopUpdaterScriptBlock = { try { - $packetSize = 4096 - + $packetSize = 9216 # 9KiB + while ($true) - { + { $stream = New-Object System.IO.MemoryStream try { @@ -947,38 +963,24 @@ $global:VirtualDesktopUpdaterScriptBlock = { $stream.SetLength($totalBufferSize) - $stream.position = 0 - - $totalBytesRead = 0 + $stream.position = 0 $buffer = New-Object -TypeName Byte[] -ArgumentList $packetSize do { - $bufferSize = $totalBufferSize - $totalBytesRead + $bufferSize = $stream.Length - $stream.Position if ($bufferSize -gt $packetSize) { $bufferSize = $packetSize } - else - { - # Save some memory operations for creating objects. - # Usually, bellow code is call when last chunk is being sent. - $buffer = New-Object -TypeName byte[] -ArgumentList $bufferSize - } - - $Param.Client.SSLStream.Read($buffer, 0, $bufferSize) - - $null = $stream.Write($buffer, 0, $buffer.Length) - - $totalBytesRead += $bufferSize - } until ($totalBytesRead -eq $totalBufferSize) + + $null = $stream.Write($Param.Client.BinaryReader.ReadBytes($bufferSize), 0, $bufferSize) + } until ($stream.Position -eq $stream.Length) $stream.Position = 0 if ($Param.RequireResize) - { - #$image = [System.Drawing.Image]::FromStream($stream) - + { $bitmap = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $stream $Param.VirtualDesktopSyncHash.VirtualDesktop.Picture.Image = Invoke-SmoothResize -OriginalImage $bitmap -NewWidth $Param.VirtualDesktopWidth -NewHeight $Param.VirtualDesktopHeight @@ -995,9 +997,9 @@ $global:VirtualDesktopUpdaterScriptBlock = { finally { $stream.Close() - } - + } } + } finally { From 7226fe0bfa69292a12dae29062ab43a3319086d7 Mon Sep 17 00:00:00 2001 From: Jean-Pierre LESUEUR Date: Fri, 21 Jan 2022 11:51:04 +0100 Subject: [PATCH 5/8] Perf: desktop bitmap pixel format switched from default (32) to 24 --- .../PowerRemoteDesktop_Server.psm1 | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 index d269835..7c351dd 100644 --- a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 +++ b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 @@ -1070,11 +1070,16 @@ $global:DesktopStreamScriptBlock = { $Screen.Bounds.Location.Y ) - $bitmap = New-Object System.Drawing.Bitmap($size.Width, $size.Height) + $bitmap = New-Object System.Drawing.Bitmap( + $size.Width, + $size.Height, + [System.Drawing.Imaging.PixelFormat]::Format24bppRgb + ) + $graphics = [System.Drawing.Graphics]::FromImage($bitmap) $graphics.CopyFromScreen($location, [System.Drawing.Point]::Empty, $size) - + return $bitmap } catch @@ -1113,7 +1118,13 @@ $global:DesktopStreamScriptBlock = { { try { - $desktopImage = Get-DesktopImage -Screen $Param.Screen + $desktopImage = Get-DesktopImage -Screen $Param.Screen + + try{ + $HostSyncHash.host.ui.WriteLine(([System.Drawing.Image]::GetPixelFormatSize($desktopImage.PixelFormat))) + }catch{ + $HostSyncHash.host.ui.WriteLine($_) + } $imageStream = New-Object System.IO.MemoryStream From 4a097064310cfb817858186bb1b3e2eb89340a0a Mon Sep 17 00:00:00 2001 From: Jean-Pierre LESUEUR Date: Fri, 21 Jan 2022 12:00:31 +0100 Subject: [PATCH 6/8] Fix: removed debug statement --- .../PowerRemoteDesktop_Server.psm1 | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 index 7c351dd..3240c84 100644 --- a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 +++ b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 @@ -1118,13 +1118,7 @@ $global:DesktopStreamScriptBlock = { { try { - $desktopImage = Get-DesktopImage -Screen $Param.Screen - - try{ - $HostSyncHash.host.ui.WriteLine(([System.Drawing.Image]::GetPixelFormatSize($desktopImage.PixelFormat))) - }catch{ - $HostSyncHash.host.ui.WriteLine($_) - } + $desktopImage = Get-DesktopImage -Screen $Param.Screen $imageStream = New-Object System.IO.MemoryStream @@ -1938,7 +1932,7 @@ function Invoke-RemoteDesktopServer # Waiting for Runspaces to finish their jobs. while ($true) { - $completed = $true + $completed = $true # Probe each existing runspaces foreach ($runspace in $runspaces) From 768fad0bac2aa9c5326a3bf89c3058c9244db75a Mon Sep 17 00:00:00 2001 From: Jean-Pierre LESUEUR Date: Fri, 21 Jan 2022 12:06:15 +0100 Subject: [PATCH 7/8] refactor: version updated --- .../PowerRemoteDesktop_Server.psd1 | Bin 8388 -> 8390 bytes .../PowerRemoteDesktop_Server.psm1 | 2 +- .../PowerRemoteDesktop_Viewer.psd1 | Bin 8458 -> 8460 bytes .../PowerRemoteDesktop_Viewer.psm1 | 5 +++-- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psd1 b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psd1 index 95fd3918326a4fdde985a7df836215a2e41418f5..90dc4211cd795604a94a7c5db6face86f11b89f8 100644 GIT binary patch delta 39 qcmX@&c+7D_5fh`?=3=Jb(%cFR5U9+cFgaIFcJeJ5q0P(WT%jo#KdT}xtPgOMqYsd0+oSu07DT&Dj4Sg=|qNNhSbT7Ji?ow$f+>_09Tg_ A^#A|> delta 33 ocmeBi>T=pp#KdU2xtPgOX7T|!8O|hzRE83UL Date: Fri, 21 Jan 2022 12:19:25 +0100 Subject: [PATCH 8/8] docs: readme updated --- README.md | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 652222c..83d187e 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ Tested on: * Windows 10 - PowerShell Version: 5.1.19041.1320 * Windows 11 - PowerShell Version: 5.1.22000.282 +Current version: **1.0.6 Stable** + ## Features https://user-images.githubusercontent.com/2520298/150001915-0982fb1c-a729-4b21-b22c-a58e201bfe27.mp4 @@ -31,11 +33,29 @@ https://user-images.githubusercontent.com/2520298/150001915-0982fb1c-a729-4b21-b * Multi-Screen (Monitor) support. If remote computer have more than one desktop screen, you can choose which desktop screen to capture. * View Only mode for demonstration. You can disable remote control abilities and just show your screen to remote peer. -## What is still beta +## Development Roadmap + +### Version 1.x (Now marked as stable) + +Version `1.x` development is now over, only bug fix and improvements will be pushed to dedicated branch. + +### Version 2.x (In progress) + +Version `2.x` development is in progress with one new big feature and one huge improvement. + +#### Feature -Version 1.0.5 Beta 6 is the last beta before final version. +Motion detection for desktop capture. Instead of capturing the whole screen, only updated screen areas will be sent to viewer thus improving considerably the streaming speed and reducing CPU usage. -No more features will be added in 1.x version, just optimization and bug fix. +#### Improvement + +A huge part of the protocol will be updated. + +The whole handshake progress will be cleaner and both Server and Viewer will respectively acknowledge their desired configuration. + +For example, instead of setting the image quality in server option (which makes no sense), it will be available from viewer option and sent to server. + +Same thing for image resizing, instead of resizing desktop image viewer-side, image will be resized server-side accordingly with viewer constraints. ## Installation @@ -305,6 +325,11 @@ Detail Fingerprint * Clipboard synchronization Viewer <-> Server added. * Server support a new option to only show desktop (Mouse moves, clicks, wheel and keyboard control is disabled in this mode). +### 21 January 2022 (1.0.6) + +* TransportMode option removed. +* Desktop streaming performance / speed increased. + ### List of ideas and TODO * 🟢 Support Password Protected external Certificates.