Tar
Tar

Reputation: 9015

System.Threading.Timer kills the PowerShell console

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?

Upvotes: 3

Views: 731

Answers (1)

Palansen
Palansen

Reputation: 311

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

Upvotes: 3

Related Questions