DOBON.NET DOBON.NETプログラミング掲示板過去ログ

シャットダウンイベント取得について

環境/言語:[Win2000、XPPro、VB.NET2003]
分類:[.NET]

いつも参考にさせていただいております。

複数インストーラを連続して呼び出すプログラムを作成しようと
考えているのですが、途中でリブートする場合があるので
シャットダウンイベントを取得しようと思っています。
※プロセス監視して終了コードも取得していますが、インストーラによって
 終了コードが統一されていないので終了コード判定ができず困っています。

シャットダウンイベントを取得しようと思っているのですが、
「イベント待ち状態」でないと、うまくイベントを取得することが
できませんでした。

バッチのような処理の時に、シャットダウンイベントを取得する
方法をどなたかご存知の方がいらっしゃったらお教え頂けないでしょうか?

※上記の「イベント待ち状態」とは、ユーザがフォーム上のボタンを
 押下するまで待っている状態のことです。

よろしくお願いします。
こんばんは、銀じぃさん。ピラルクです。

> バッチのような処理の時に、シャットダウンイベントを取得する
> 方法をどなたかご存知の方がいらっしゃったらお教え頂けないでしょうか?

GUIスレッドから ProcessのWaitForExit()とかしているからでしょうか?

Private Sub MyForm_SomeEvent(...
Dim t As New Thread(New ThreadStart(AddressOf ExecInstaller))
t.Start()
End Sub

GUIスレッドからワーカースレッドをあげて、そこでProcess起動&待機すれば
どうでしょう?
おはようございます。ピラルクさん返信ありがとうございます。

私の質問内容が悪かったみたいでうまく伝わってないようですね。
申し訳ありません。

インストーラを起動し、その終了コードを取得することはできています。
また、ご指摘いただいた別スレッド起動もしています。
インストーラの終了コードからインストーラがリブートすることを
判断することが大変な(インストーラによって終了コードが異なる)為、
OSのシャットダウンイベントを取ろうと考えました。
しかし、こちらのTIPSに載ってる方法でやってもうまくいかなかったのです。

※フォームを表示しイベント待ち状態であれば、
 OSのシャットダウンイベントを取得できました。

起動から終了までイベント待ちのないプログラムでは
シャットダウンイベントが取れないものなのでしょうか?
(Sub Main → フォーム表示 → Timer_Tick内で処理を記述しています)
こんばんは、銀じぃさん。ピラルクです。

> 起動から終了までイベント待ちのないプログラムでは
> シャットダウンイベントが取れないものなのでしょうか?

SystemEvents.SessionEndingイベントの正体は、おそらく
WM_QUERYENDSESSIONというOSがトップレベルウィンドウに
対してブロードキャストするメッセージによって発火される
イベントでしょうから、イベントを取れない状態のFormでは
検知し損ねるでしょう。
いわゆる「メッセージポンプが回っている」状態にしておか
ないとダメです。つまり、Timer_Tickも含めてそのFormの
Eventで処理を止めてはいけません。

> ※フォームを表示しイベント待ち状態であれば、
> OSのシャットダウンイベントを取得できました。

これが出来ているならイベント自体ではなく、Processの扱い
方か、質問文に登場していない何か別のところ(Timer_Tick内?)
に原因があるのではという気がします。
「フォーム表示」し「別スレッド起動」していて、かつ
「イベント待ちのないプログラム」というのが想像つきません。

(補)
イベント待ちのないプログラムとも言える
コンソールアプリケーションであれば、
Declare Function SetConsoleCtrlHandler Lib "kernel32" _
(ByVal e As ControlEventHandler, ByVal add As Boolean) As Boolean
で、シャットダウンを検知することができます。
ピラルクさんへ。

ご指摘いただいた内容から、何となくはイメージが掴めてるけど
具体的にどうしたらいいのか検討がつきませんでした。

実際のプログラムに似た簡単なテストプログラム(TP)を
作成してみたんですがやっぱりシャットダウンイベントが取得できませんでした。
以下にソースを載せておきます。※コメント少なくてごめんなさい。
(今回はわざとループ終了判定にシャットダウン発生のフラグを用いています)

このようなやり方が変なのかな・・・。

<モジュールファイル>
Module Module1
 Sub Main()
 Dim fm As New Form1
  fm.ShowDLG()
  fm = Nothing
 End Sub
End Module

<フォームファイル>
Imports Microsoft.Win32

Public Class Form1
 Inherits System.Windows.Forms.Form

 Private m_IsShutdown As Boolean

' " Windows フォーム デザイナで生成されたコード "

 Public Sub ShowDLG()
  m_IsShutdown = False
  Timer1.Enabled = True
  Me.ShowDialog()
 End Sub

 Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick

  ' この中でずっと複数のインストーラ起動を行います
  Do
   ' インストーラ起動
   XXXXX

   Application.DoEvents()
  Loop Until m_IsShutdown

  Me.Close()
 End Sub

 ' -----↓どぼん!TIPS流用↓-----
 Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
  AddHandler SystemEvents.SessionEnding, AddressOf SystemEvents_SessionEnding
 End Sub

 Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
  RemoveHandler SystemEvents.SessionEnding, AddressOf SystemEvents_SessionEnding
 End Sub

 Private Sub SystemEvents_SessionEnding(ByVal sender As Object, ByVal e As SessionEndingEventArgs)
  If e.Reason = SessionEndReasons.SystemShutdown Then
   m_IsShutdown = True
  End If
 End Sub
 ' -----↑どぼん!TIPS流用↑-----
End Class
こんばんは、銀じぃさん。ピラルクです。

>  ' この中でずっと複数のインストーラ起動を行います
>  Do
>   ' インストーラ起動
>   XXXXX
>
>   Application.DoEvents()
>  Loop Until m_IsShutdown

問題はこの部分にあるのは確かです。ですが重要な部分が
XXXXXなので、以下ちょっと当てずっぽうになります。
「ずっと……起動を行います」とコメントにありますが、
何回も何回も起動するわけじゃないですよね。(^^;
だからXXXXXは1回しか走らないということで、XXXXXで
処理が止まっている(=同期処理になっている)と予想。
そうだとしたらTimer処理に入っていることも特に意味を
持たない(or遅延の為?)ように思いますので省きました。

<フォームファイル>
Imports Microsoft.Win32
Imports System.Threading '<<-----------追加

Public Class Form1
 Inherits System.Windows.Forms.Form

 Private m_IsShutdown As Boolean

' " Windows フォーム デザイナで生成されたコード "

 Public Sub ShowDLG()
  m_IsShutdown = False
  Me.ShowDialog()
 End Sub

Private Sub ExecInstaller()
' インストーラ起動
XXXXX

Me.Close()
End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler SystemEvents.SessionEnding, AddressOf SystemEvents_SessionEnding

Dim t As New Thread(AddressOf ExecInstaller)
t.Start()
End Sub

Private Sub Form1_Closed(---------------------どぼん!TIPS流用
Private Sub SystemEvents_SessionEnding(-------どぼん!TIPS流用

End Class

最初の案どおり全体をごそっとスレッド化したのですが、
これはうまく動きますか?
(インストーラの特性もからんだ話かも知れないので
ちょっと当方では判断つきません)
こんにちは。ピラルクさん。

> 問題はこの部分にあるのは確かです。ですが重要な部分が
> XXXXXなので、以下ちょっと当てずっぽうになります。
> 「ずっと……起動を行います」とコメントにありますが、
> 何回も何回も起動するわけじゃないですよね。(^^;

ごめんなさい。長文になるのを避けて肝心なことを伝えていませんでした。
インストーラはサーバ側から任意の数ほど配信されてきて
クライアントで連続的に実行されます。その為、何回も回るんです(^^;

結局、固定のインストーラ終了コードがきたらリブートするという
仕様的に決まりました。
個人的には今回のリブートイベントが取得できなかった理由が
分からなかったのが非常に残念ですが"解決済み"とさせていただきます。

ピラルクさん、いろいろアドバイスありがとうございました。

P.S.何か分かりましたら別途報告致します。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板