Timerイベント内でWindowsのシャットダウンを正常に行うには?


たき  2003-08-20 23:53:38  No: 108194

設定した時間が経過したらWindowsをシャットダウンするプログラムを
作っているのですが、正常にシャットダウンされません。
フォームにタイマーコントロールを貼り付け、
Interval = 1000
に設定し、テキストボックスに入力された値を
タイマーイベント内で−1していき、
0以下になったらWindowsをシャットダウンするようにしてあります。
ただ、下のようなコードだとうまくいきません。
現象としては、全てのウィンドウが閉じ、
壁紙だけになった状態から、何も起こらないんです。
マウスカーソルと壁紙だけ表示されており、マウスカーソルは動くのですが、
キー入力等は一切反応しない状態になります。
ですが、
Timer1.Enabled = Falseの行のコメントをはずすと、
正常にシャットダウンされるんです。
ただ、それがなぜなのかが分かりません。

Private Sub Timer1_Timer()
    nLeftTime = nLeftTime - 1

    If nLeftTime <= 0 Then
'        Timer1.Enabled = False   '←ここの行

        ' Windowsの終了
        Call ExitWindowsEx(enmShutdown, 0)
    End If
End Sub

なぜこういう質問をしたかというと、
別の環境では、コメントアウトしないと正常に動かず、
コメントをはずすと、シャットダウンされないという現象が起こるらしく、
もしかしたらTimer1.Enabledとは別の原因があるのかもしれないと
思ったからです。

もしTimer1.Enabled = Falseのやり方が合っているのでしたら、
なぜこれを行わないと正常に終了しないのかを
教えていただけないでしょうか。
どうか宜しくお願いいたします。


Take1  2003-08-21 01:15:50  No: 108195

Timer1.Enabled = False

の位置を

Call ExitWindowsEx(enmShutdown, 0)

の後にもってきては?


たき  2003-08-21 18:29:42  No: 108196

Take1さんありがとうございます。
現在コメントアウトしないと正常に動かないPCが
手元に無いため、
Timer1.Enabled = Falseを実行しないと
正常に動かないPCでしか試せれませんが、
Timer1.Enabled = Falseを
Call ExitWindowsEx(enmShutdown, 0)
の前にしても後にしても、
正常にシャットダウンされました。

それから、もう一台のPCで実行してみたところ、
コメントアウトしても、しなくても正常にシャットダウンされる
PCも見つかりました。ますます原因が分からなくなってきました。
それぞれの環境で違う点を挙げておきます。
・Timer1.Enabled = Falseを実行する→正常にシャットダウン
  Timer1.Enabled = Falseをコメントアウト→壁紙だけ残る
    PC1  WinXP Pro      :  Visual Studio 6.0 Pro SP5
    PC2  Win2k Pro SP3  :  Visual Studio 6.0 Pro SP5

・Timer1.Enabled = Falseを実行する→反応なし(らしい)
  Timer1.Enabled = Falseをコメントアウト→正常にシャットダウン
    PC3  Win2k Pro      :  開発環境無し
      ※相手先のPCのため、現象の詳細を確認中

・Timer1.Enabled = Falseを実行・コメントアウトにかかわらず
  正常にシャットダウン
    PC4  Win2k Pro SP4  :  Visual Basic 6.0 Pro SP5  及び  
                               Visual C++ 6.0 Pro SP5

おそらくDLL等のバージョンの違いのために
現象が違うのだと思いますが、
どのDLLなのか分かりません。
もしかしたらDLLが原因ではないかもしれませんし。
もしお分かりになる方がいらっしゃいましたら、
どうか教えてもらえないでしょうか。


さわ  2003-08-21 18:47:22  No: 108197

横レス失礼します。
シャットダウンの特権の変更はされていますでしょうか?
OSによっては特権の変更をしないと正しく終了しませんょ。
カレントのプロセスからOpenProcessTokenでアクセストークンのハンドルを
LookupPrivilegeValueでLUIDを取得し
_TOKEN_PRIVILEGESの属性をSE_PRIVILEGE_ENABLEDに変更して
AdjustTokenPrivilegesにて調整してみて下さい。


たき  2003-08-21 19:15:07  No: 108198

さわさんありがとうございます。
シャットダウン特権の設定は以下のように別関数にて行っています。
このため、どのPC・OSでも
Timer1.Enabled = Falseのコメント・非コメントさえ変えれば、
正常にシャットダウンされます。

Private Function SetPrivillege(SE_NAME As String) As Boolean
    
    Dim lngRet      As Long
    
    Dim hToken      As Long
    Dim udtLUID     As LUID
    Dim udtTokenPrv As TOKEN_PRIVILEGES

    'カレントプロセスに関連付けられているアクセストークンを開く
    If OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken) Then

        ' ローカル一意識別子(LUID)を取得し、指定された特権名をローカルに表現
        If LookupPrivilegeValue(vbNullString, SE_NAME, udtLUID) Then

            udtTokenPrv.PrivilegeCount = 1
            udtTokenPrv.Privileges(0).pLuid = udtLUID
            udtTokenPrv.Privileges(0).Attributes = SE_PRIVILEGE_ENABLED
    
            ' 指定された特権を有効にする
            If AdjustTokenPrivileges(hToken, 0, udtTokenPrv, LenB(udtTokenPrv), ByVal 0&, ByVal 0&) Then
                SetPrivillege = True
            End If
        End If
    End If

    ' トークンハンドルを閉じる
    If hToken Then CloseHandle hToken

End Function


Take1  2003-08-21 19:59:29  No: 108199

Timer1.Enabled = False  を  Call ExitWindowsEx(enmShutdown, 0)
の前にあるとコードの通り正確に実行(コードの発行と処理がほぼ同時に)されると  Call EXit〜  は呼び出される事なくタイマーイベントは終了されます。

たまたま実行されたりすることがあるのは、おそらくタイムラグだとおもいます。
コンピュータの環境条件等によっては、まず  Timer1.Enabled = False  が呼び出されそして実行・・される前に  Call EXit〜  が呼び出され、直後に  Timer1.Enabled = False  が実行されることがあります。
タイマーイベントはあつかいかた次第で不具合がおこりやすいのであなどれません。
対処として、例えばタイマーイベント内の処理をできるだけ減らすとか、
インターバルを大きくするとか、タイマーイベントの外に
プロシージャをつくって、それをタイマーイベントから呼び出してユーザー実行中のプログラムをすべて強制終了させて  Call EXit〜  する、という工夫も必要かもしれません。


たき  2003-08-21 20:35:48  No: 108200

Take1さんありがとうございます。
すみません、私の説明が下手でうまく伝わっていないみたいです。
正常にシャットダウン、全てのウィンドウが消えて壁紙だけ残る、
反応なし(シャットダウン処理が走らない)、の3つの現象ですが、
Timer1.Enabled = Falseの行のコメント・非コメントの状態にもよりますが、
同じ状態で行えば、毎回必ず起こります。
たまたま正常に終了したりしなかったりということはありません。
Timer1.Enabled = False  が  Call ExitWindowsEx(enmShutdown, 0)  の
前にあっても後にあっても、PC1・2・4は全て毎回必ず正常終了します。
PC1・2に関しては、Timer1.Enabled = Falseを
Form_Unloadイベント内に移動させても、
タイマーイベント内でコメントアウトしたときと同じく
壁紙だけが残り、無反応となります。
ですが、この状態で電源ボタン長押しにて電源オフを行い、
再度起動しても、スキャンディスクが走らないことから、
Windows自体は既に終了したことになっていると思われます。


さわ  2003-08-21 23:42:35  No: 108201

まず?ExitWindowsExが正常に実行されているのかは
確認されましたか?
if ExitWindowsEx <> True then 
   nCode := GetLastError;
   Debug.Print SysErrorMessage( nCode )
end if 
みたいな?Apiのエラーであるのか?
タイムラグ等のイベント連鎖が関係しているのか?
いかがでしょう。。


ねろ  2003-08-22 01:17:56  No: 108202

はずしているかもしれませんが...
私も以前リモートシャットオフでトラブッたことがあります。
そのときはw98でしたが。
結局ネットワークがうまく切断されないと言う結論になり、
Shell "c:\windows\net use * /del"を
(一秒ほど待つ)
Call ExitWindowsEx(EWX_FORCE + EWX_SHUTDOWN, rsv)
の前に入れました。
最終的には完全には解決されませんでしたが、windowsのshutt down
の最後の画面でハングする回数は入れない場合の1/10位になりました。


Take1  2003-08-22 01:29:58  No: 108203

IF内のイベントはTimer1.Enabled = False後も
処理されるようですね。失礼しましたm(_ _)m
以前失敗した事を錯誤して記憶してました。

自分の環境(XP,HOME,ノートパソコン と 98,ノートパソコン)では
たきさん のタイマーイベント方法でたくさんのアプリケーションを起動させたままでもなんの問題もなく終了します。

調べてみて情報ください。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加