2011-09-14 52 views
1

を実行していない私は、次のコードを実行した場合、イベントアクションが実行されます。のPowerShell:フォームでの仕事イベントアクション

$Job = Start-Job {'abc'} 
Register-ObjectEvent -InputObject $Job -EventName StateChanged ` 
    -Action { 
      Start-Sleep -Seconds 1 
      Write-Host '*Event-Action*' 
      } 

文字列「イベント - アクション」が表示されます。

私はフォームを使用し、ボタンをクリックすることにより、上記のコードを起動した場合、

イベントアクションが実行されません:私は、もう一度ボタンをクリックしたときに

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
$Form1 = New-Object Windows.Forms.Form 
$Form1.Add_Shown({ 
    $Form1.Activate() 
}) 
$Button1 = New-Object System.Windows.Forms.Button 
$Button1.Text = 'Test' 
$Form1.Controls.Add($Button1) 
$Button1.Add_Click({ 
    Write-Host 'Test-Button was clicked' 
    $Job = Start-Job {'abc'} 
    Register-ObjectEvent -InputObject $Job -EventName StateChanged ` 
     -Action { 
       Start-Sleep -Seconds 1 
       Write-Host '*Event-Action*' 
       } 
}) 
$Form1.ShowDialog() 

のみ、最初のイベントアクションがあります実行される。

3回目のクリックで、2番目のイベントアクションが実行されます。

複数のクリックを連続して行うと、結果が予測できなくなります。私は右上のボタンでフォームを閉じると

はさらに、

最後の「オープン」イベントアクションが実行されます。

注:PSコンソールが特定の状況でのみ

文字列を表示するので、テストのためのPowerShell ISEは、好ましいことです。

誰かが私にここで何が起こっているのか手がかりを与えてくれますか?

ありがとうございます!


ニミツン。

説明していただきありがとうございますが、Formで何らかのアクションが発生するまで、StateChangedイベントが発生しない、またはメインスクリプトに表示されない理由はわかりません。私はそれを私に説明する別の試みに感謝します。

私が達成したいのは、PowerShellとFormsを使ったマルチスレッドのようなものです。スクリプトがユーザーにフォームを示し

私の計画は次のとおりです。

ユーザーは入力をしてボタンをクリックします。 ユーザーの入力に基づいて、一連のジョブがStart-Jobで開始され、各ジョブに対してStateChangedイベントが登録されます。

ジョブが実行されている間、ユーザーはフォームで任意のアクションを実行できます(ボタンを使用してジョブを停止するなど)。フォームは必要なときに再描画されます。

スクリプトは、フォームまたはその子コントロールによって起動されたすべてのイベントに反応します。

また、スクリプトは各ジョブのStateChangedイベントに反応します。

StateChangedイベントが発生すると、各ジョブの状態が検査され、すべてのジョブが「完了」状態の場合、ジョブの結果はReceive-Jobで取得され、ユーザーに表示されます。

このすべてがのstateChangedイベントがメインスクリプトに表示されていないことを除いて正常に動作します。

上記はまだ私のお気に入りの解決策であり、これを実装する方法が分かっている場合は、教えてください。

それ以外の場合は、少なくともユーザーにマルチスレッドの感触を与える回避策に頼ることになります。次の例に示します。

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
$Form1 = New-Object Windows.Forms.Form 
$Form1.Add_Shown({ 
    $Form1.Activate() 
}) 
$Button1 = New-Object System.Windows.Forms.Button 
$Button1.Text = 'Test' 
$Form1.Controls.Add($Button1) 
$Button1.Add_Click({ 
    $Form1.Focus() 
    Write-Host 'Test-Button was clicked' 
    $Job = Start-Job {Start-Sleep -Seconds 1; 'abc'} 
    Do { 
     Start-Sleep -Milliseconds 100 
     Write-Host 'JobState: ' $Job.State 
     [System.Windows.Forms.Application]::DoEvents() 
    } 
    Until ($Job.State -eq 'Completed') 
    Write-Host '*Action*' 
}) 
$Form1.ShowDialog() 

答えて

0

Powershellジョブの状態プロパティは読み取り専用です。つまり、実際にジョブを開始する前にジョブ状態を何かに構成することはできません。 statechangedイベントを監視しているときは、clickイベントが再び発生し、状態が '実行中'から '完了済み'に変更されてスクリプトブロックが実行されるまで状態が '見られる'まで発生しません。これは、フォームを閉じるときにスクリプトブロックが実行される理由もあります。

次のスクリプトは、イベントを監視する必要がなくなり、状態を監視します。状態が '実行中'のときに 'statechanged'コードを実行したいと思う。また

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
$Form1 = New-Object Windows.Forms.Form 
$Form1.Add_Shown({ 
    $Form1.Activate() 
}) 
$Button1 = New-Object System.Windows.Forms.Button 
$Button1.Text = 'Test' 
$Form1.Controls.Add($Button1) 

$Button1.Add_Click({ 
$this.Enabled = $false 
    Write-Host $Job.State " - (Before job started)" 
    $Job = Start-Job {'abc'} 
    Write-Host $Job.State " - (After job started)" 
     If ($Job.State -eq 'Running') { 

       Start-Sleep -Seconds 1 
       Write-Host '*Doing Stuff*' 
       } 

    Write-Host $Job.State " - (After IF scriptblock finished)" 
[System.Windows.Forms.Application]::DoEvents() 
$this.Enabled = $true 

}) 

$Form1.ShowDialog() 

、注意して行:

$this.Enabled = $false 
[System.Windows.Forms.Application]::DoEvents() 
$this.Enabled = $true 

これらの行は、ボタンがクリックイベントをキューに入れていないことを確認してください。あなたは明らかに 'write-host'行を削除することができます。それらを残しておけば、スクリプトが実行されるときの状態の変化を見ることができます。

これが役に立ちます。

0

は、この 'enduring mystique' 形式を組み合わせるの程度(StackOverflowの)質問と回答がたくさんあります(またはWPF).NETイベントとイベントPowerShellの(のようなEngineEventsObjectEventsWmiEvents):

彼らはすべて2つの1点に降りてくるされています。でも、複数のスレッドの設定があり、1つのスレッドで二つの異なる「リスナー」はあります。スクリプトが(ShowDialogまたはDoEventsを使用して)フォームイベントを受信する準備ができたら、同時に.NETイベントを聴くことはできません。また、逆の場合:Start-Sleepなどのコマンドを処理しているときにスクリプトが.NETイベントに対して開いている場合、またはWait-EventまたはWait-Jobなどのコマンドを使用して.NETイベントをリッスンすると、フォームはフォームイベントをリッスンできません。つまり、.NETイベントまたはフォームイベントが、作成しようとしている.NETリスナーと同じスレッド内にあるため、キューに入れられているということです。 nimizenの例のように、最初の亀頭で正しいと思われるように見える場合、フォームはバックグラウンドワーカーの状態をチェックしているときに他のフォームイベント(ボタンクリック)に無関係で、ボタンをクリックしなければなりません。まだそれがまだ‘*Doing Stuff’であるかどうかを調べるためにもう一度。これを回避するには、背景作業者の状態を継続的にチェックしながら、ループの中でDoEventsメソッドを組み合わせることを検討することもできますが、どちらも良い方法ではありません。Use of Application.DoEvents() 私は[runspacefactory]::CreateRunspace()を使用してのみ扱うことができると思う他のスレッドでの1つのスレッドを持っていると、フォームイベントを(例えばTextChangedのように)トリガーして、フォームコントロールを同期させることができます。

(別の方法であり、私は熱心な方法を学習すると作業の例を見れば。)

フォームに例:WPF例えば

Function Start-Worker { 
    $SyncHash = [hashtable]::Synchronized(@{TextBox = $TextBox}) 
    $Runspace = [runspacefactory]::CreateRunspace() 
    $Runspace.ThreadOptions = "UseNewThread"     # Also Consider: ReuseThread 
    $Runspace.Open() 
    $Runspace.SessionStateProxy.SetVariable("SyncHash", $SyncHash)   
    $Worker = [PowerShell]::Create().AddScript({ 
     $ThreadID = [appdomain]::GetCurrentThreadId() 
     $SyncHash.TextBox.Text = "Thread $ThreadID has started" 
     for($Progress = 0; $Progress -le 100; $Progress += 10) { 
      $SyncHash.TextBox.Text = "Thread $ThreadID at $Progress%" 
      Start-Sleep 1          # Some background work 
     } 
     $SyncHash.TextBox.Text = "Thread $ThreadID has finnished" 
    }) 
    $Worker.Runspace = $Runspace 
    $Worker.BeginInvoke() 
} 

[Void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
$Form = New-Object Windows.Forms.Form 
$TextBox = New-Object Windows.Forms.TextBox 
$TextBox.Visible = $False 
$TextBox.Add_TextChanged({Write-Host $TextBox.Text}) 
$Form.Controls.Add($TextBox) 
$Button = New-Object System.Windows.Forms.Button 
$Button.Text = "Start worker" 
$Button.Add_Click({Start-Worker}) 
$Form.Controls.Add($Button) 
$Form.ShowDialog() 

を、以下を参照してください。 Write PowerShell Output (as it happens) to WPF UI Control