3

When I run this in a PowerShell console:

$callback = [System.Threading.TimerCallback]{
    param($state)
}

$timer = [System.Threading.Timer]::new($callback, $null,
    [timespan]::Zero,
    [timespan]::FromSeconds(1))

Then once $callback gets called (I made sure that this is the root cause, by changing the dueTime constructor's parameter from [timespan]::Zero to longer delays), the whole console process crashes, saying that powershell has stopped working.

What could be the reason? how can I overcome this?

mklement0
  • 382,024
  • 64
  • 607
  • 775
Tar
  • 8,529
  • 9
  • 56
  • 127

1 Answers1

3

The error is:

There is no Runspace available to run scripts in this thread. You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspace type.

And stems from

System.Management.Automation.ScriptBlock.GetContextFromTLS()

The delegate is the issue. This is how I got it working:

$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

$runspacedDelegate = [RunspacedDelegateFactory]::NewRunspacedDelegate($callback, [Runspace]::DefaultRunspace)

$timer = [System.Threading.Timer]::new(
    $runspacedDelegate,
    $null,
    [timespan]::Zero,
    [timespan]::FromSeconds(1))

I borrowed NewRunspacedDelegate from

https://www.powershellgallery.com/packages/ContainerTools/0.0.1

Palansen
  • 311
  • 2
  • 7
  • 1
    Wow, what a fuss... impressive, but not sure if it's worth it.. why does it have to be that hard, just to send a delegate? – Tar Dec 15 '18 at 07:48
  • Btw, how did you manage to get the error? the console just crashes for me. – Tar Dec 15 '18 at 07:50
  • It's recorded in the event log. That is, the full text error is output from PowerShell 6. – Palansen Dec 15 '18 at 08:28