You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

244 lines
8.1 KiB

#!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 ';';
};
$helper = @'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Management.Automation.Runspaces;
public class RunspacedDelegateFactory
{
public static Delegate NewRunspacedDelegate(Delegate _delegate, Runspace runspace)
{
Action setRunspace = () => Runspace.DefaultRunspace = runspace;
return ConcatActionToDelegate(setRunspace, _delegate);
}
private static Expression ExpressionInvoke(Delegate _delegate, params Expression[] arguments)
{
var invokeMethod = _delegate.GetType().GetMethod("Invoke");
return Expression.Call(Expression.Constant(_delegate), invokeMethod, arguments);
}
public static Delegate ConcatActionToDelegate(Action a, Delegate d)
{
var parameters =
d.GetType().GetMethod("Invoke").GetParameters()
.Select(p => Expression.Parameter(p.ParameterType, p.Name))
.ToArray();
Expression body = Expression.Block(ExpressionInvoke(a), ExpressionInvoke(d, parameters));
var lambda = Expression.Lambda(d.GetType(), body, parameters);
var compiled = lambda.Compile();
return compiled;
}
}
'@
add-type -TypeDefinition $helper
#region console
$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';
#endregion console
$consoleid = $args[0];
if ([string]::IsNullOrEmpty($consoleid)) {
$consoleid= "<punkshell_consoleid>"
};
$pipeName = "punkshell_ps_consolemode_$consoleid";
"pipename: $pipeName";
$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream(
$pipeName,
[System.IO.Pipes.PipeDirection]::In,
1,
[System.IO.Pipes.PipeTransmissionMode]::Byte,
[System.IO.Pipes.PipeOptions]::Asynchronous
);
#$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName);
;
# Define the callback function for when a client connects
$callback = [System.AsyncCallback]{
param($asyncResult);
$se = $asyncResult.AsyncState.Sync;
$ps = $asyncResult.AsyncState.Pipeserver;
$pn = $asyncResult.AsyncState.Pipename;
Write-Host "Client connected - $pn";
# End the asynchronous wait operation
;
$ps.EndWaitForConnection($asyncResult);
#??
;
# You can now perform read/write operations with the client
# For example, create a StreamReader and StreamWriter
;
$streamReader = New-Object System.IO.StreamReader($ps);
#$streamWriter = New-Object System.IO.StreamWriter($pipeServer);
#$streamWriter.AutoFlush = $true;
try {
$message = $streamReader.ReadLine();
Write-Host "Received: $message";
;
#$asyncResult.Message = $message;
;
} catch {
Write-Error "Error during communication: $($_.Exception.Message)";
} finally {
# sever connection with client but keep the named pipe
if ($streamReader -ne $null) {
Write-Host "streamreader closing";
$streamReader.Close();
Write-Host "streamreader closed";
}
Write-Host "Client disconnecting. $pn";
$ps.Disconnect();
Write-Host "Client disconnected. $pn";
#$ps.Disconnect();
#[System.Console]::Out.Flush();
;
};
write-host "HERE";
$se.set();
# Optionally, you can call BeginWaitForConnection again to listen for another client
# if your server is designed for multiple connections over time.
# $pipeServer.BeginWaitForConnection($callback, $null)
$ps.BeginWaitForConnection($callback, $null)
;
};
$syncEvent = New-Object System.Threading.ManualResetEvent($false);
$runspacedDelegate = [RunspacedDelegateFactory]::NewRunspacedDelegate($callback, [Runspace]::DefaultRunspace);
$loop = 0;
while ($loop -lt 15) {
Write-Host "Waiting for client connection on pipe '$pipeName'...";
# Begin the asynchronous wait for a client connection
;
$state = [PSCustomObject]@{
Loop = $loop
Sync = $SyncEvent
Pipeserver = $pipeServer
Pipename = $pipename
Message = ""
};
$x = $pipeServer.BeginWaitForConnection($runspacedDelegate, $state );
$syncEvent.WaitOne(10000);
# $x | Get-Member | write-host
;
write-host "msg: $(${x}.AsyncState.Message)"
if ($x.IsCompleted) {
write-host "completed";
};
$SyncEvent.reset();
$loop += 1;
#$cli = New-Object System.IO.Pipes.NamedPipeClientStream($pipeName);
#$cli.w
}
# Keep the script running to allow the asynchronous operation to complete
# In a real-world scenario, you might have a loop or other logic here.
#Read-Host "Press Enter to exit the server."
# Clean up
$pipeServer.Dispose();