搜尋此網誌

2024年7月23日 星期二

dotnet 建立 windows 服務,監控資料夾的變動

緣起:


    今天看到我們研發寫的一支監控檔案目錄下的檔案有沒有被更動的程式,我學到了 BackgroundService 跟 FileSystemWatcher 的使用,還有用 sc.exe 來建立服務,覺得很有趣,所以想寫個文章來學習。

    我是在 windows 下操作專案的,改天有空的話會再去 linux 上面試試。

 

dotnet 建立 worker 專案:

    
    參考資料

    我在專案資料夾打開 cmd,輸入指令來建立一個叫 TestWork 的專案

dotnet new work -o TestWork

    然後用 vs code 開啟專案,需要為專案加入 Microsoft.Extensions.Hosting.WindowsServices 的套件

dotnet add package Microsoft.Extensions.Hosting.WindowsServices

    worker 專案有預設的 Program.cs 跟 Work.cs 範本,我們先修改 Program

using TestWork;

var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureServices((hostContext, services)=>{
    services.AddHostedService<Worker>();
});

builder.UseWindowsService();
var host = builder.Build();
host.Run();

    第 5 行 : 加入 Work class 的服務,它繼承 BackgroundService Class。
    第 8 行 : 加入 windows service 的支援。


    然後是我們的 Work.cs

namespace TestWork;

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly string _logFolderPath = Path.Combine(AppContext.BaseDirectory, "log");
    private readonly string _watchFolderPath = @"C:\Users\qaz09\OneDrive\桌面\TestWatch";
    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        Directory.CreateDirectory(_logFolderPath);

        using (var watcher = new FileSystemWatcher())
        {
            watcher.Path = _watchFolderPath;
            watcher.IncludeSubdirectories = true;
            watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.Size | NotifyFilters.DirectoryName;
            watcher.Filter = "*.*";
            watcher.EnableRaisingEvents = true;

            watcher.Changed += OnChange;
            watcher.Created += OnChange;
            watcher.Deleted += OnChange;
            watcher.Renamed += OnChange;

            while (!stoppingToken.IsCancellationRequested)
            {
                await Task.Delay(1000, stoppingToken);
            }
        }
    }

    private void OnChange(object sender, FileSystemEventArgs args)
    {
        string logFilePath = Path.Combine(_logFolderPath, "log.txt");
        using (StreamWriter writer = new StreamWriter(logFilePath, true))
        {
            writer.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd:hh:MM:ss")} | {args.ChangeType} | {args.FullPath} | {args.Name}");
        }
    }
}

    第 6 行 : 紀錄 log 檔的位置,用 AppContext.BaseDirectory 這個泛用的方法來取得。
    第 7 行 : 要監控的資料夾位置,寫死,桌面的一個 TestWatch 資料夾。
    第 13 行 : HostService 跑起來會進入的 function,要回傳 Task。
    第 15 行 : 沒有 log 資料夾的話建一個。
    第 17 行 : 這篇的主角,FileSystemWatcher,用它監控資料夾內的變化,它有繼承 System.ComponentMode.Component,這 class 有實作 IDisposable。
    第 19 行 : 指定監控的資料夾路徑。
    第 20 行 : 包含子資料夾。
    第 21 行 : 設定監控什麼類型的變化,用 NotifyFilters 這個 Enum 來設定。
    第 22 行 : 設定檔案名稱篩選字串,預設是 "*.*"。
    第 23 行 : 設定是否啟用元件。
    第 25~28 行 : 設定監聽事件,當目錄下有發生檔案(或目錄)內容被更動、新增、重新命名、刪除的話,觸發我們寫的 OnChange 事件。
    第 37~44 行 : OnChange 事件,它會傳進 FileSystemEventArgs 類型的參數,可以從它的成員變數取得有用的資料,像是這次的變動是什麼、檔案的完整路徑。我這邊是紀錄這些變化跟發生的時間,寫到 log.txt 裡。


用 TestWork 專案來建立服務:


    接著發布專案到桌面

dotnet publish -o ..\..\TestWorkPublish

    再來會用到 sc.exe,以管理者開啟 cmd,然後用 sc.exe 來建立專案的服務,我把它叫 birdshiutest

sc.exe create "birdshiutest" binpath="C:\Users\qaz09\OneDrive\桌面\TestWorkPublish\TestWork.exe"


    可以在那邊看到我們建的 birdshiutest,再來就啟動它


    然後到桌面的那個 TestWatch 資料夾,我丟了一張照片進去,然後再把它給刪掉,然後回去看 TestWorkPublish 資料夾,會多一個 log 資料夾,點進去會有個 log.txt 檔


    成功 !! 

    如果想把服務砍掉的話,可以下

sc.exe delete birdshiutest

沒有留言:

張貼留言