使Powershell以脚本模式响应Register-ObjectEvent事件

问题描述 投票:0回答:1

我有一个简单的Powershell脚本,我在Powershell ISE中写道。它的主旨在于它将一个命名管道作为执行动作的信号来监视,同时监视其老板进程。当boss-process退出时,脚本也会退出。简单。

在努力让命名管道在Powershell中工作而不崩溃之后,我设法获得了工作代码,如下所示。然而,虽然这在Powershell ISE和交互式终端中运行良好,但我却无法将其作为一个独立的脚本工作。

$bosspid = 16320

# Create the named pipe
$pipe = new-object System.IO.Pipes.NamedPipeServerStream(
    -join('named-pipe-',$bosspid),
    [System.IO.Pipes.PipeDirection]::InOut,
    1,
    [System.IO.Pipes.PipeTransmissionMode]::Byte,
    [System.IO.Pipes.PipeOptions]::Asynchronous
)

# If we don't do it this way, Powershell crashes
# Brazenly stolen from github.com/Tadas/PSNamedPipes
Add-Type @"
    using System;
    public sealed class CallbackEventBridge
    {
        public event AsyncCallback CallbackComplete = delegate {};
        private void CallbackInternal(IAsyncResult result)
        {
        CallbackComplete(result);
        }
        public AsyncCallback Callback
        {
        get { return new AsyncCallback(CallbackInternal); }
        }
    }
"@

$cbbridge = New-Object CallBackEventBridge
Register-ObjectEvent -InputObject $cbbridge -EventName CallBackComplete -Action {
        param($asyncResult)
        $pipe.EndWaitForConnection($asyncResult)
        $pipe.Disconnect()
        $pipe.BeginWaitForConnection($cbbridge.Callback, 1)
        Host-Write('The named pipe has been written to!')
}

# Make sure to close when boss closes
$bossproc = Get-Process -pid $bosspid -ErrorAction SilentlyContinue
$exitsequence = {
    $pipe.Dispose()
    [Environment]::Exit(0)
}
if (-Not $bossproc) {$exitsequence.Invoke()}
Register-ObjectEvent $bossproc -EventName Exited -Action {$exitsequence.Invoke()}

# Begin watching for events until boss closes
$pipe.BeginWaitForConnection($cbbridge.Callback, 1)

第一个问题是脚本在执行任何有意义的操作之前终止。但是延迟执行的结束,例如while($true)循环,-NoExit标志,pause命令,或者甚至是为此目的而制作的特定命令,如Wait-Event,将导致进程保持开放,但仍然不会使它响应事件。

powershell asynchronous scripting
1个回答
0
投票

我放弃了以“正确”的方式做到这一点,而是恢复使用包含在while-true块和Job控件中的同步代码。

$bosspid = (get-process -name notepad).id

# Construct the named pipe's name
$pipename = -join('named-pipe-',$bosspid)
$fullpipename = -join("\\.\pipe\", $pipename) # fix SO highlighting: "

# This will run in a separate thread
$asyncloop = {
    param($pipename, $bosspid)
    # Create the named pipe
    $pipe = new-object System.IO.Pipes.NamedPipeServerStream($pipename)

    # The core loop
    while($true) {
        $pipe.WaitForConnection()
        # The specific signal I'm using to let the loop continue is
        # echo m > %pipename%
        # in CMD. Powershell's echo will *not* work. Anything other than m
        # will trigger the exit condition.
        if ($pipe.ReadByte() -ne 109) {
            break 
        }
        $pipe.Disconnect()
        # (The action this loop is supposed to perform on trigger goes here)
    }
    $pipe.Dispose()
}

# Set up the exit sequence
$bossproc = Get-Process -pid $bosspid -ErrorAction SilentlyContinue
$exitsequence = {
    # While PS's echo doesn't work for passing messages, it does
    # open and close the pipe which is enough to trigger the exit condition.
    &{echo q > $fullpipename} 2> $null 
    [Environment]::Exit(0)
}
if ((-Not $bossproc) -or $bossproc.HasExited) { $exitsequence.Invoke() }

# Begin watching for events until boss closes
Start-Job -ScriptBlock $asyncloop -Name "miniloop" -ArgumentList $pipename,$bosspid
while($true) {
    Start-Sleep 1
    if ($bossproc.HasExited) { $exitsequence.Invoke() }
}

这段代码现在运行得很好,完成了我需要的工作。

© www.soinside.com 2019 - 2024. All rights reserved.