Hey, Scripting Guy!

Scripting Guys が皆さんの質問にお答えします

Hey, Scripting Guy!

TechNet コラムへようこそ。このコラムでは、よく寄せられるシステム管理スクリプトに関する質問に Scripting Guys がお答えします。システム管理スクリプトについて質問がある場合は、scripter@microsoft.com (英語のみ) までお送りください。すべての質問に回答することはできないかもしれませんが、可能な限り対応いたします。

詳細情報

Hey, Scripting Guy! カテゴリ別アーカイブ

Hey, Scripting Guy! 日付別アーカイブ

Hey, Scripting Guy! ダウンロード

Spacer

*

Windows PowerShell を使用してアプリケーションの終了時に通知する方法はありますか

Hey, Scripting Guy! Question

Scripting Guy さん、よろしくお願いします。残念なことに頻繁にクラッシュする社内アプリケーションがあります。このアプリケーションがクラッシュすると、プログラムを再起動するために、非常に長い手順を踏む必要があります。Windows PowerShell を使用して、このアプリケーションがクラッシュしたときに通知する方法はありますか。

-- OI

SpacerHey, Scripting Guy! AnswerScript Center

OI さん、こんにちは。私たちは "今日はどの質問にも回答しない" とひそかに考えていました。そして、実際に回答していませんでした。でも、ここで回答することにします。衝撃的でしょ。

失礼しました。少し調子に乗ってしまいました。今朝は興奮で少し目が回っただけです。オズの魔法使いで、"ディンドン、魔女は死んだよ" を歌いながら踊っているシーンをご存知でしょうか。私たちもそういう気持ちになったのです。今週、編集者は Microsoft Management Summit (MMS) に出席するためにサンディエゴに行っています。おかげで、恐怖の赤ペン チェックから一時的に逃れることができました。長い間、単語のスペルを間違える機会をうかがっていましたが、それも今ならできます。そして、今なら文法を間違えることもできます。まったく意味をなさない段落、いや、"コラム" を執筆する機会もうかがっていました。そうです、そして、この機会に、長年の夢を実現しているのです。しかし少なくとも今は、編集者に 2 分ごとに「それは一体どういう意味か」と問い詰められて邪魔されることなく、意味のないコラムを執筆できます。

さあ、みんなで歌いましょう。ディンドン、魔女は死んだよ...と。

ごめんなさい。まだ少し目が回っています。

大変興味深いことに、長い間やってみたかったことがもう 1 つあります。それは、Windows PowerShell を使用して、特定のプロセスの終了時に通知を受ける方法を紹介することです。ところが、どういうわけか、編集者はそれもさせてくれませんでした。でも今は、彼女に私たちを止めることはできません。

: 悪魔ですって。"悪魔" とは言いすぎです。でも、それなら、私たちも彼女が悪魔だと言わざるを得ません。

実のところ、Windows PowerShell でイベント通知を操作するには、少し注意が必要です。というのも、Windows PowerShell の Get-WMIObject コマンドレットではイベント通知クエリがサポートされていないからです (イベント通知クエリの詳細については、この Scripting Guys Webcast (英語) をご覧ください)。では、なぜ、Get-WMIObject コマンドレットではイベント通知クエリがサポートされていないのでしょうか。正直なところ、私たちにもわかりません。"あの人" が今まで私たちに調べさせてくれなかったのです。とはいえ、今重要なのは、プロセスの終了時に通知を受け取るために、従来の WMI を使用できないことです。代わりに、他の有効ないくつかのオプションを利用する必要があります。

こうしたオプションの 1 つに、Get-Process コマンドレットがあります。OI さんが説明してくれた状況では、任意のプロセスが終了したときに通知が必要というわけではなく、特定のプロセス (たとえば、電卓) が終了した場合にのみ通知が必要です。この要件を満たすイベント通知スクリプトは、次に示すように、たった 3 行のコードで実現できます。

$a = get-process calc
$a.waitforexit()
"Calculator has stopped running."

まず、Get-Process コマンドレットを使用して、Calc という名前のプロセスにバインドします (ちなみに、この名前は、実行可能ファイルの名前からファイル拡張子を除いたものです)。ここでは、Calc という名前のプロセスが 1 つしか存在しないことを前提としていることに注意してください。電卓のインスタンスを複数実行している場合はどうなるでしょうか。この場合は、この最初のコード行は動作します。電卓の複数のインスタンスで構成されているコレクションが返されますが、スクリプトは 2 行目でエラーになります。でも、心配しないでください。すぐに、汎用的なプロセス監視スクリプトをご紹介します。

Calc プロセスのハンドルを取得したら、WaitForExit() メソッドを呼び出します。

$a.waitforexit()

このように処理するとどうなるでしょうか。このコード行により、スクリプトが "ブロック" されます。つまり、スクリプトは、そこで停止し、該当プロセスが終了するまで待機します。該当プロセスが終了したら、3 行目と 4 行目のコードを使用して、電卓が終了したことをエコー バックします。

この段階では、このスクリプトは問題なく動作します。スクリプトを実行して、ご自分の目で確かめてください。ただし、このスクリプトは完璧というわけではありません。たとえば、電卓が停止するたびに、スクリプトも停止します。つまり、電卓と監視スクリプトの両方を再起動する必要があります。

そうです、これは、まさに編集者が皆さんに何かをさせるときの方法です。でも思い出してください。編集者は外出中です。つまり、今日のコラムでは、次のような別の方法を紹介することができます。

$b = 1

do 
    {
        $a = get-process calc
        $a.waitforexit()
        Invoke-Item c:\windows\system32\calc.exe

    } 
while ($b -eq 1)

ここで行っている処理は何でしょうか。ここでは、変数 $b が 1 である限り実行される Do ループを設定しています ($b は常に 1 になるため、要するに、このループは永久に実行されます)。このループ内では、再度 Calc プロセスにバインドし、WaitForExit() メソッドを呼び出します。ただし、電卓が終了したときの処理は異なります。メッセージを画面に表示する代わりに、Invoke-Item コマンドレットを使用してアプリケーションを再起動します。

Invoke-Item c:\windows\system32\calc.exe

電卓が再起動したら、ループの先頭に戻り、監視を再開します。OI さんの言う、社内アプリケーションを再起動するための "非常に長い手順" が一体どういうものなのかはわかりませんが、その処理をプログラムで行うことが可能であれば、この方法を使用することをお勧めします。もちろん、アプリケーションを再起動するだけでなく、電卓の再起動が必要だったことを報告することもできます。たとえば、メッセージを画面に表示したり、ログ ファイルやイベント ログなどに記録することができます。これは皆さんにお任せします。

: 既に説明したように、上記のスクリプトは永久に実行されるように設計されています。このスクリプトを停止するには、Windows PowerShell の特定のインスタンスを終了する方法しかありません。たとえばキーボードの Q キーを押すと停止するようにスクリプトを変更することができますが、その作業は複雑なので、今日は説明する時間がなさそうです。

最初に紹介したスクリプトと同様、この洒落たプロセスの再起動スクリプトが動作するのは、該当プロセスの 1 つのインスタンスだけが実行されている場合です。つまり、電卓のインスタンスが複数ある場合は、問題が発生します。さらに、Get-Process コマンドレットには WaitForExit() メソッドが含まれているため、このスクリプトは、プロセスに対してしか動作しません。その他のエンティティでは、必ずしも同じようになるとは限りません。

このことを念頭に置いて、.NET Framework を使用する、汎用的なイベント監視スクリプトを見てみましょう。まず、コードを紹介してから、スクリプトの動作について簡単に説明します。

$a = 0

$timespan = New-Object System.TimeSpan(0, 0, 1)
$scope = New-Object System.Management.ManagementScope("\\.\root\cimV2")
$query = New-Object System.Management.WQLEventQuery `
    ("__InstanceDeletionEvent",$timespan, "TargetInstance ISA 'Win32_Process'" )
$watcher = New-Object System.Management.ManagementEventWatcher($scope,$query)

do 
    {
        $b = $watcher.WaitForNextEvent()
        $b.TargetInstance.Name
    } 
while ($a -ne 1)

このスクリプトでは、削除されたプロセスを 1 秒ごとに確認しています。どうして、秒単位で確認することがわかるのでしょうか。それは次の System.Timespan オブジェクトの構成方法を見ればわかります。

$timespan = New-Object System.TimeSpan(0, 0, 1)

ご覧のとおり、新しいオブジェクトを作成するときに、0、0、1 という 3 つのパラメータを含めました。最初の 0 は時間、2 番目の 0 は分、1 は秒を表します。削除されたプロセスを 15 分 30 秒ごとに確認する場合はどうなるでしょうか。その場合は、次のコード行を使用します。

$timespan = New-Object System.TimeSpan(0, 15, 30)

では、どうして、削除された "プロセス" を探していることがわかるのでしょうか。WQLEventQuery を設定したときに何を要求したかを見ればわかります。

$query = New-Object System.Management.WQLEventQuery `
    ("__InstanceDeletionEvent",$timespan, "TargetInstance ISA 'Win32_Process'" )

おわかりでしょうか。ここでは __InstanceDeletionEvent クラスのインスタンスを探していますが、これらのインスタンスがプロセス (つまり、Win32_Process クラスのメンバ) の場合だけが該当します。

: そうですね。これは、__InstanceDeletionEvent や TargetInstance などの用語の大まかな説明にすぎませんね。詳細については、既に紹介した Scripting Guys Webcast (英語) をご覧ください。

System.Management.ManagementEventWatcher クラスのインスタンスを作成したら、別の永久ループに入り、プロセスが終了するまで待機します。プロセスが終了するたびに、次のコード行を使用して、プロセスの Name プロパティ値を返しています。

$b.TargetInstance.Name

もちろん、ここではもっと洒落た処理を行うことは可能です。たとえば、終了したプロセスが電卓のインスタンスだった場合にのみ警告を発行できます。ただし、こうした変更については、皆さん自身でやる機会を提供しましょう。

OI さん、ご質問ありがとうございました。実は、編集者は、MMS への参加報告をする予定になっています。その報告がこのコラムと同じくらい面白いものになりそうか、ですって。とんでもありません。でも、少なくとも、各単語のスペルが間違っているということはないでしょう。

編集者注 : 驚くべきことは、編集者が、水晶玉 (ラップトップ コンピュータと呼ばれることもあります) を持ち歩いているということです。彼女は、この水晶玉を駆使して、遠く離れた場所から、他の Scripting Guys を監視できます。ここでは、Scripting Guys が編集者の権力に抵抗したことで受ける結果については説明しませんが、面白くない結果になることは確かです。けれども、彼らは多くのことを学んでいます・・・。

Microsoft Management Summit 2007 (または、少なくともサンディエゴの天気) に関する編集者による心奪われるようなすばらしい報告がスクリプト センターで公開されるのをお楽しみに。


ページのトップへページのトップへ