うぃろぅ.log

140字で綴りきれない日々の徒然備忘録

【Windows サービス】 Visual Studio 2017でWindows サービスを作る その2

うぃろぅです。

前回の記事から引き続きWindows サービスを作る。
次で終わるといったな。あれは嘘じゃ。

FileSystemWatcherについて調べていたらまた長くなったので、その2は監視処理を開始するところまでとした。
今回で終わるはずだったのに…。

その3がこちら。検知したファイルパスをテキストファイルに出力する。

vviilloovv.hatenablog.com

フォルダ監視

以下のサイトがめちゃくちゃわかりやすい。

Windows サービス - カスタマイズ可能な FileSystemWatcher Windows サービスの作成
フォルダ、ファイルの変更を監視する - .NET Tips (VB.NET,C#...)

Watcher.csのソースを表示し、作りこんでいく。

初期状態のソースはこちら。

using System.ServiceProcess;

namespace WatchService
{
    public partial class Watcher : ServiceBase
    {
        public Watcher()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
        }

        protected override void OnStop()
        {
        }
    }
}

ゴール設定

今回は、

  • Test_, Sample_ で始まるフォルダの作成、名前変更を検知
  • 検知したフォルダのパスをテキストファイルに出力

をゴールとする。

フォルダ監視設定

FileSystemWatcherというクラスがあり、これが大体何とかしてくれる。

フォルダを監視するメソッドをStartFileWatchとして作りこんでいく。

  • OnStart
    サービス開始時に呼ばれる。
    StartFileWatchを呼び出す処理だけ追加。
protected override void OnStart(string[] args)
{
        // フォルダ監視開始
        StartFileWatch();
}
  • StartFileWatch さっき貼ったサイトを参考に淡々と作るだけかと思ったら違った。
    監視対象のフィルターがstring型なので複数条件に対応できない。
    以下を参考にループさせることにした。

Second Room: .NET:FileSystemWatcherで複数フィルタ指定

こんな感じに。

/// <summary>
/// フォルダ監視処理
/// </summary>
private void StartFileWatch()
{
    // 命名規則を設定
    // 複数条件はスペースを入れず「|」で区切る
    var folderNamingRule = "Test_*|Sample_*";

    var filters = folderNamingRule.Split('|');
    foreach(var filter in filters)
    {
        // インスタンスの設定
        var watcher = new FileSystemWatcher
        {
            Path = @"C:\Visual Studio\WatchService\Watch", 
            Filter = filter,
            IncludeSubdirectories = false,
            NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName
            Inter
        };

        // 監視バッファサイズの設定
        watcher.InternalBufferSize = 16 * 1024;

        // 監視イベントの設定
        watcher.Created += Watcher_Created;
        watcher.Renamed += Watcher_Renamed;

        // 監視開始
        watcher.EnableRaisingEvents = true;
    }
}

インスタンスの設定値は、

  • Path : 監視対象のフルパス。
  • Filter : 監視対象の条件。複数条件不可。
  • IncludeSubdirectories : サブフォルダを監視対象に含めるか。
    デフォルトでfaultだが、念のため。
  • NotifyFilter : 監視対象の種類。こちらは複数条件可。ずるい。
  • InternalBufferSize : 内部バッファのサイズ。後述。
  • 監視イベント : 作成とリネーム。他には削除とか更新とかがある。

となっている。

InternalBufferSizeについて

FileSystemWatcherクラスだが、変更を確実に監視してくれるのかというと、全然そんなことはない。

FileSystemWatcher.InternalBufferSize プロパティとは何? Weblio辞書

この記事を読めば大体のことがわかると思う。
つまりは別の処理とか負荷とかでメモリがオーバーフローすると変更が通知されなくなる。
のでこれを適当に大きくすればその取りこぼしが減るよねって話。

もちろんこの値を大きくするとその分メモリを食うことになるので、
どれだけ大きくするかはパフォーマンスとの兼ね合いとなる。

今回は例なので16 * 1024バイト(16kB)としたが、
一気に1000件とか置かない限り規定値の8 * 1024でも概ね検知してくれる…はず。

  • Watcher_Created
  • Watcher_Renamed

Visual Studio 2017 はとんでもなくインテリジェントで、
watcher.Created += Watcher_Created;を書いた後タブを叩くと以下のメソッドを吐き出してくれる。

private void Watcher_Created(object sender, FileSystemEventArgs e)
{
    throw new NotImplementedException();
}

Watcher_Renamedの方も同様。

あとはここに処理を書き込んでいけばいいということになる。

また長くなった

単純にテキストファイルに書き出して終わり!
って書こうと思っていたのだけれど、どうせなら非同期で動かすかとか考えていたら絶対それだけでもう1エントリー分の文章量には達する。

なのでその3に続けることにした。
次回で絶対終わるから!!ほんとだよ!!!!

画像貼ってなかった。
今こんな感じ。

f:id:vviilloovv:20181116174120p:plain

ではまた。