#!SEMICOLONS must be placed after each command as scriptdata needs to be sent to powershell directly with the -c parameter! ; if ($PSVersionTable.PSVersion.Major -le 5) { # For Windows PowerShell, we want to remove any PowerShell 7 paths from PSModulePath #snipped from https://github.com/PowerShell/DSC/pull/777/commits/af9b99a4d38e0cf1e54c4bbd89cbb6a8a8598c4e #Presumably users are supposed to know not to have custom paths for powershell desktop containing a 'powershell' subfolder?? ; $env:PSModulePath = ($env:PSModulePath -split ';' | Where-Object { $_ -notlike '*\powershell\*' }) -join ';'; }; $consoleModeSource = @" using System; using System.Runtime.InteropServices; public class NativeConsoleMethods { [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr GetStdHandle(int handleId); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool GetConsoleMode(IntPtr hConsoleOutput, out uint dwMode); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool SetConsoleMode(IntPtr hConsoleOutput, uint dwMode); public static uint GetConsoleMode(bool input = false) { var handle = GetStdHandle(input ? -10 : -11); uint mode; if (GetConsoleMode(handle, out mode)) { return mode; } return 0xffffffff; } public static uint SetConsoleMode(bool input, uint mode) { var handle = GetStdHandle(input ? -10 : -11); if (SetConsoleMode(handle, mode)) { return GetConsoleMode(input); } return 0xffffffff; } } "@ ; [Flags()] enum ConsoleModeInputFlags { ENABLE_LINE_INPUT = 0x0002 ENABLE_ECHO_INPUT = 0x0004 }; if (!('NativeConsoleMethods' -as [System.Type])) { Add-Type $consoleModeSource } function psmain { param ( [validateSet('enableRaw', 'disableRaw')] [string]$Action ); # $inputflags = Get-ConsoleMode -StandardInput; if (!('NativeConsoleMethods' -as [System.Type])) { Add-Type $consoleModeSource } $inputFlags = [NativeConsoleMethods]::GetConsoleMode($true); $resultflags = $inputflags; if (($inputflags -band [ConsoleModeInputFlags]::ENABLE_LINE_INPUT) -eq [ConsoleModeInputFlags]::ENABLE_LINE_INPUT) { #cooked mode $initialstate = "cooked"; if ($action -eq "enableraw") { #disable cooked flags $disable = [uint32](-bnot [uint32][ConsoleModeInputFlags]::ENABLE_LINE_INPUT) -band ( -bnot [uint32][ConsoleModeInputFlags]::ENABLE_ECHO_INPUT); $adjustedflags = $inputflags -band ($disable); $resultflags = [NativeConsoleMethods]::SetConsoleMode($true,$adjustedflags); } } else { #raw mode $initialstate = "raw"; if ($action -eq "disableraw") { #set cooked flags $adjustedflags = $inputflags -bor [ConsoleModeInputFlags]::ENABLE_LINE_INPUT -bor [ConsoleModeInputFlags]::ENABLE_ECHO_INPUT; $resultflags = [NativeConsoleMethods]::SetConsoleMode($true,$adjustedflags); } } #return in format that can act as a tcl dict #write-host "startflags: $inputflags initialstate: $initialstate action: $Action endflags: $resultflags"; }; # psmain 'enableRaw' ; $consoleid = $args[0]; if ([string]::IsNullOrEmpty($consoleid)) { $consoleid= "" }; $pipeName = "punkshell_ps_consolemode_$consoleid"; "pipename: $pipeName" # Create the NamedPipeServerStream $pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream( $pipeName, [System.IO.Pipes.PipeDirection]::In, 1, [System.IO.Pipes.PipeTransmissionMode]::Message, [System.IO.Pipes.PipeOptions]::Asynchronous ); ; #$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName); ; # Create a synchronization object (ManualResetEvent) to signal completion ; $syncEvent = New-Object System.Threading.ManualResetEvent($false) # Define the callback function for BeginWaitForConnection $connectionCallback = [System.AsyncCallback]{ param($ar,$s); # Get the NamedPipeServerStream from the AsyncResult object ; $server = $ar.AsyncState; # End the asynchronous wait operation ; $server.EndWaitForConnection($ar); Write-Host "Client connected!"; # Create a new runspace to handle client communication ; $runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace(); $runspace.Open(); # Create a PowerShell pipeline within the runspace ; $powershell = [System.Management.Automation.PowerShell]::Create(); $powershell.Runspace = $runspace; $scriptBlock = { param($pipeStream); $reader = New-Object System.IO.StreamReader($pipeStream); #$writer = New-Object System.IO.StreamWriter($pipeStream); #$writer.AutoFlush = $true; $message = $reader.ReadLine(); Write-Host "Received from client: $message"; #$response = "Server received: $message" #$writer.WriteLine($response) #Write-Host "Sent to client: $response" # Disconnect the pipe to allow new connections if desired ; $pipeStream.Disconnect(); }; # Add the script block to the PowerShell pipeline and pass the pipe stream ; $powershell.AddScript($scriptBlock).AddArgument($server); # Invoke the pipeline asynchronously ; $asyncResult = $powershell.BeginInvoke(); # You can do other work here while the client communication happens in the runspace ; ; # Wait for the pipeline to complete and close the runspace $powershell.EndInvoke($asyncResult); $powershell.Dispose(); $runspace.Close(); $runspace.Dispose(); Write-Host "Client communication handled. Waiting for next connection..."; $s.Set(); # Begin waiting for the next connection; #$server.BeginWaitForConnection($callback, $server); ; } $global:keep_listening = $true; while ($global:keep_listening) { # Begin waiting for a client connection asynchronously ; $runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace(); $runspace.Open(); # Create a PowerShell pipeline within the runspace ; $powershell = [System.Management.Automation.PowerShell]::Create(); $powershell.Runspace = $runspace; $scriptblock = { param($p,$s); Write-Host "Waiting for client connection on pipe: $p"; $p.BeginWaitForConnection($connectionCallback, $p,$s); $s.WaitOne(10000); } $powershell.AddScript($scriptBlock) [void]$powershell.AddParameter('p',$pipeName); [void]$powershell.AddParameter('s',$syncEvent); $asyncResult = $powershell.BeginInvoke(); # You can do other work here while the client communication happens in the runspace ; write-host "interim" ; # Wait for the pipeline to complete and close the runspace $powershell.EndInvoke($asyncResult); $powershell.Dispose(); $runspace.Close(); $runspace.Dispose(); write-host "looping" }; #$pipeServer.BeginWaitForConnection($connectionCallback, $pipeServer); Write-Host "Server shutting down."; $pipeServer.Dispose(); #try { # while ($true) { # #"Waiting for connection on '$pipeName'"; # $pipeServer.WaitForConnection(); # #"Connection established"; # $pipeReader = New-Object System.IO.StreamReader($pipeServer); # #$pipeWriter = New-Object System.IO.StreamWriter($pipeServer); # #$pipeWriter.AutoFlush = $true; # $request = $pipeReader.ReadLine(); # # "Received request: $request"; # if ($request -eq "exit") { # "consolemode_server.ps1 Exiting"; # exit; # } elseif ($request -eq "") { # #"Empty input"; # $pipeServer.Disconnect(); # #"Disconnected"; # continue; # } elseif ($request -eq $none) { # "Remote disconnected before sending"; # $pipeServer.Disconnect(); # "Disconnected"; # continue; # } elseif ($request -eq "enableraw") { # #$result = psmain 'enableRaw'; # $null = psmain 'enableRaw' # # "Sending result: '$result'"; # #$pipeWriter.Write($result); # $pipeServer.Disconnect(); # continue; # } else { # "consolemode_server.ps1 ignoring request: $request"; # $pipeServer.Disconnect(); # continue; # } # } #} #finally { # $pipeServer.Dispose(); #};