簡體   English   中英

使Powershell在腳本模式下響應Register-ObjectEvent事件

[英]Make a Powershell respond to Register-ObjectEvent events in script mode

我有一個在Powershell ISE中編寫的簡單Powershell腳本。 其要點是,它監視命名管道進行寫操作,以作為執行操作的信號,同時監視其上司進程。 當老板進程退出時,腳本也會退出。 簡單。

在努力使命名管道在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 while($true)類的技巧來延遲執行的結束導致進程保持打開狀態,但仍然不會使其響應事件。

我放棄了“正確”的方式,而是轉而使用包裹在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() }
}

該代碼現在可以正常工作,並且可以完成我需要的工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM