Windowsサービスの作り方講座 その3

これまでUPしたエントリ。

Windowsサービスの作り方講座 その0
Windowsサービスの作り方講座 その1
Windowsサービスの作り方講座 その2

その3です。今回の予定は。

  • 前回作ったひな型にちょっとだけユーザロジック追加
  • Visual Studioを使ったデバッグのやり方紹介

このぐらいまでやってみようかなと。

前回作ったひな型にちょっとだけユーザロジックを追加してみる

まあ、1行のコメント行だけじゃなくて、何かしてみようかなと。んー、とネタを考えて、一定の間隔でPINGを投げて、対象が生きているかどうかを調べてファイルに吐く、なんてのを書いてみよう。

こんなフィーリングになった。*1

Public Class MyService
    Private goingToDie As Boolean = False
    Private mainThread As Threading.Thread
    Private args() As String
    Private WriteLog As Action(Of String)
    Private Sub Runner()
        Dim currentTime As DateTime = Now()
        Dim previousActionTime As DateTime = currentTime - (New TimeSpan(0, My.Settings.IntervalMinutes, 0))
        Dim stateWriter As IO.StreamWriter = My.Computer.FileSystem.OpenTextFileWriter(My.Settings.TargetIP & " state.txt", True)
        Dim targetIsAlive As Boolean = False
        Dim stateReport As String = ""
        WriteLog("Enter Runner")
        Do Until goingToDie
            '何かやりたいこと
            currentTime = Now()
            If currentTime - previousActionTime > (New TimeSpan(0, My.Settings.IntervalMinutes, 0)) Then
                targetIsAlive = My.Computer.Network.Ping(My.Settings.TargetIP)
                stateReport = String.Format("{0}:{1} is {2}.", Now(), My.Settings.TargetIP, IIf(targetIsAlive, "alive", "dead"))
                stateWriter.WriteLine(stateReport)
                stateWriter.Flush()
                previousActionTime = currentTime
            End If
            Threading.Thread.Sleep(1)
        Loop
        stateWriter.Close()
        stateWriter.Dispose()
        WriteLog("Exit Runner")
    End Sub

    Protected Overrides Sub OnStart(ByVal args() As String)
        ' サービスを開始するコードをここに追加します。このメソッドによって、
        ' サービスが正しく実行されるようになります
        WriteLog = AddressOf New Diagnostics.EventLog("Application", My.Computer.Name, Me.ServiceName).WriteEntry
        WriteLog("OnStart")
        Me.args = args
        mainThread = New Threading.Thread(AddressOf Runner)
        mainThread.Start()
    End Sub

    Protected Overrides Sub OnStop()
        ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。
        WriteLog("OnStop")
        goingToDie = True
        mainThread.Join()
    End Sub

End Class

My.Settingsに調べたいIP(TargetIP)と調べる間隔(IntervalMinutes)を指定して、それを使っているので、プロジェクトのプロパティからこんな設定も必要。

一分おきぐらいにしてみた。IPはまあ、テスト用なのでこんな感じでw で、動かすとこんなファイルができる。

C:\Windows\System32>dir *state.txt
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 01C2-2408 です

 C:\Windows\System32 のディレクトリ

2011/01/09  11:50                44 127.0.0.1 state.txt
               1 個のファイル                  44 バイト
               0 個のディレクトリ  47,825,354,752 バイトの空き領域

できる場所はなんとC:\Windows\System32! カレントディレクトリがアセンブリの配置してあるディレクトリではないという点に注意が必要。きちんとドライブレターから完全に書いてあるパスで修飾しないとこんな事になります。
で、メモ帳で開くと中身はこんな。

2011/01/09 11:50:28:127.0.0.1 is alive.
2011/01/09 11:51:28:127.0.0.1 is alive.
2011/01/09 11:52:28:127.0.0.1 is alive.
2011/01/09 11:53:28:127.0.0.1 is alive.

うむ、予定通り。ということで、ユーザロジックの追加はこんなもんにしておこう。

Visual Studioのデバッガを使ってみる

最初の方で、F5キーを押しても動かんぞ!というのをやったんだけど、じゃあVisual Studioデバッグ機能は使えないのか? というと、ちゃんと使える。動いている状態でステップ実行や変数のウォッチ、書き換えもちゃんとできる。でも、F5キーから始めちゃダメで、違うお作法でやらないといけない。

まずは、サービスを立ち上げておいて、そこからVisual Studioの操作を始める。ツールメニューから「プロセスにアタッチ」を選ぶ。

これを選ぶと、こんなダイアログがでてくる。

ここから、これからデバッグしたいサービスのプロセスを選ぶんだけど、下の2つのチェックボックス、「すべてのユーザーからのプロセスを表示する」と「すべてのセッションのプロセスを表示する」をチェックしないと出てきません。これをチェックして探すと…

あった! で、このプロセスを選んで右下のアタッチボタンをクリック。するとこんな窓が!

ここは「異なる資格情報で再起動」をクリック。するともう一回確認が。

ここも「はい」をクリック。すると。

ここまで戻る。で左上の方に(管理者)とかひっそりと書いてあって管理者として実行されている事がわかる。ここからもう一回、プロセスにアタッチするまでをやり直すと。

一見なにもかわっていないようだけど、左上の方に(実行中)とある。これで今動いているプロセスをデバッガでいろいろできるようになっている。試しに一時停止ボタンを押してみよう。

これを押すと…

こんな感じに。いつもの黄色い現在実行行をだすには、ブレイクポイントを設定してからF5キーを押せばいい。すると、

こうなる。こうなったらあとはいつもと同じ。イミディエイトも使えるし、値の変更もできる。
デバッグを終わるには停止ボタンを押す。ここで停止ボタンを押しても、サービスプロセスは停止しません。そのまま動き続けるので、もし、修正・リビルドをやりたいなら、サービスを先に止める。でないとサービスが動いていてアセンブリが使用中なので怒られます。このように。

まあ、当然といえば当然の結果。ちゃんと終わらせた後ならビルドが通るようになります。

例によって、zipを貼っておくます。
MyService (3).zip 直

*1:実はちょっと前回から直した個所もある。ログの出力文言、前は"Entry Runner"になってたんだけど、ちょっとヘンかなと思いなおして"Enter Runner"に修正。まあ、大した話ではないのかもしれないけど、ちょっと恥ずかしい英語だったかなーと;;