首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 建立 Windows Service 完整教學:檔案監控、效能計數器、安裝部署

     編輯:關於C語言

前言

Windows Service(舊稱 NT Service)是在背景長期執行、無使用者介面、支援開機自動啟動的應用程式。本文手把手教學建立一個檔案監控服務,涵蓋 FileSystemWatcher、PerformanceCounter、專案安裝類別、installutil / PowerShell 部署、偵錯技巧與現代化替代方案。

專案建立步驟

  1. Visual Studio → 新增專案 → Windows Service (.NET Framework) 或 Worker Service (.NET Core/5+)。
  2. 專案名稱:FileMonitorService。
  3. 自動產生 ServiceBase 衍生類別 FileMonitorService 與 Program.cs。

核心服務程式碼


using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;

namespace FileMonitorService
{
    public partial class FileMonitorService : ServiceBase
    {
        private FileSystemWatcher _watcher;
        private PerformanceCounter _createCounter, _deleteCounter, _renameCounter, _changeCounter;
        private bool _paused;

        public FileMonitorService()
        {
            InitializeComponent();
            ServiceName = "FileMonitorService";
            CanPauseAndContinue = true;
            CanStop = true;
            AutoLog = true; // 寫入 Windows 事件日誌
        }

        protected override void OnStart(string[] args)
        {
            _paused = false;
            InitializeCounters();
            InitializeWatcher();
            EventLog.WriteEntry("檔案監控服務已啟動", EventLogEntryType.Information);
        }

        protected override void OnStop()
        {
            _watcher?.Dispose();
            ResetCounters();
            EventLog.WriteEntry("檔案監控服務已停止", EventLogEntryType.Information);
        }

        protected override void OnPause()
        {
            _paused = true;
            _watcher.EnableRaisingEvents = false;
        }

        protected override void OnContinue()
        {
            _paused = false;
            _watcher.EnableRaisingEvents = true;
        }

        private void InitializeCounters()
        {
            const string category = "File Monitor Service";
            if (!PerformanceCounterCategory.Exists(category))
            {
                var ccds = new CounterCreationDataCollection
                {
                    new CounterCreationData("Files Created", "建立檔案數", PerformanceCounterType.NumberOfItems32),
                    new CounterCreationData("Files Deleted", "刪除檔案數", PerformanceCounterType.NumberOfItems32),
                    new CounterCreationData("Files Renamed", "重命名檔案數", PerformanceCounterType.NumberOfItems32),
                    new CounterCreationData("Files Changed", "修改檔案數", PerformanceCounterType.NumberOfItems32)
                };
                PerformanceCounterCategory.Create(category, "檔案監控計數器", PerformanceCounterCategoryType.SingleInstance, ccds);
            }
            _createCounter = new PerformanceCounter(category, "Files Created", false) { ReadOnly = false };
            _deleteCounter = new PerformanceCounter(category, "Files Deleted", false) { ReadOnly = false };
            _renameCounter = new PerformanceCounter(category, "Files Renamed", false) { ReadOnly = false };
            _changeCounter = new PerformanceCounter(category, "Files Changed", false) { ReadOnly = false };
        }

        private void ResetCounters()
        {
            foreach (var c in new[] { _createCounter, _deleteCounter, _renameCounter, _changeCounter })
            {
                if (c?.RawValue > 0) c.IncrementBy(-c.RawValue);
            }
        }

        private void InitializeWatcher()
        {
            string path = ConfigurationManager.AppSettings["MonitorPath"] ?? @"C:\Monitor";
            if (!Directory.Exists(path)) Directory.CreateDirectory(path);

            _watcher = new FileSystemWatcher(path)
            {
                IncludeSubdirectories = true,
                NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirName | NotifyFilters.LastWrite | NotifyFilters.Size,
                EnableRaisingEvents = true
            };
            _watcher.Created += (s, e) => { if (!_paused) _createCounter.Increment(); };
            _watcher.Deleted += (s, e) => { if (!_paused) _deleteCounter.Increment(); };
            _watcher.Renamed += (s, e) => { if (!_paused) _renameCounter.Increment(); };
            _watcher.Changed += (s, e) => { if (!_paused) _changeCounter.Increment(); };
            _watcher.Error += (s, e) => EventLog.WriteEntry($"Watcher 錯誤: {e.GetException().Message}", EventLogEntryType.Error);
        }
    }
}

安裝類別


using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;

[RunInstaller(true)]
public class ProjectInstaller : Installer
{
    private ServiceProcessInstaller _processInstaller;
    private ServiceInstaller _serviceInstaller;

    public ProjectInstaller()
    {
        _processInstaller = new ServiceProcessInstaller { Account = ServiceAccount.LocalSystem };
        _serviceInstaller = new ServiceInstaller
        {
            ServiceName = "FileMonitorService",
            DisplayName = "檔案監控服務",
            Description = "監控指定目錄檔案變更並記錄效能計數器",
            StartType = ServiceStartMode.Automatic,
            DelayedAutoStart = true // 延遲啟動,加快開機速度
        };
        Installers.AddRange(new Installer[] { _processInstaller, _serviceInstaller });
    }
}

App.config 設定




  
    
  
  
    
      
        
      
    
  

建置與安裝部署

.NET Framework (installutil)


# 開發人員命令提示字元 (管理員)
cd bin\Debug
installutil FileMonitorService.exe
# 移除
installutil /u FileMonitorService.exe

.NET Core / 5+ (Worker Service + SC.exe)


# 發布單一檔案
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true

# 使用 sc.exe 建立服務
sc.exe create FileMonitorService binPath= "C:\Deploy\FileMonitorService.exe" start= auto DisplayName= "檔案監控服務"
sc.exe start FileMonitorService
sc.exe stop FileMonitorService
sc.exe delete FileMonitorService

PowerShell (跨版本通用)


# 安裝
New-Service -Name "FileMonitorService" -BinaryPathName "C:\Deploy\FileMonitorService.exe" -DisplayName "檔案監控服務" -StartupType Automatic -Description "監控檔案變更"
Start-Service FileMonitorService

# 移除
Stop-Service FileMonitorService -Force
Remove-Service FileMonitorService

偵錯技巧

  1. 附加偵錯:Visual Studio → 偵錯 → 附加至處理序 → 選擇服務進程 (需管理員)。
  2. 模擬啟動:在 Program.cs 加入條件編譯:

static void Main()
{
#if DEBUG
    var svc = new FileMonitorService();
    svc.OnStart(null);
    Console.WriteLine("按 Enter 停止...");
    Console.ReadLine();
    svc.OnStop();
#else
    ServiceBase.Run(new FileMonitorService());
#endif
}
  1. 事件檢視器:Windows Logs → Application,來源 FileMonitorService 查看啟停/錯誤日誌。
  2. 效能監視器:perfmon.msc → 加入「File Monitor Service」計數器即時觀察。

常見坑點與解決

問題原因解決
服務啟動後立即停止OnStart 拋出未處理例外try-catch 包裹 OnStart,寫入 EventLog
FileSystemWatcher 漏事件內部緩衝區溢位 (預設 8KB)InternalBufferSize = 65536,避免密集操作目錄
網路磁碟機監控失效SMB 通知機制不穩改用輪詢 或 FileSystemWatcher + 定時掃描補漏
效能計數器需管理員建立 Category 需提權安裝程式以管理員執行,或預先用腳本建立 Category
單一檔案發布無法載入設定App.config 不會內嵌改用 appsettings.json + ConfigurationBuilder

現代化替代方案

  • Generic Host (Worker Service):.NET Core 3.0+ 推薦,IHostedService + BackgroundService,支援 DI、設定、日誌、優雅關機,跨平台 (Linux systemd / Docker)。
  • BackgroundService + FileSystemWatcher:同樣邏輯,只需繼承 BackgroundService 覆寫 ExecuteAsync。
  • 容器化:Docker + --restart=always 替代傳統 Windows Service。

總結

掌握 ServiceBase 生命週期、FileSystemWatcher 事件模型、PerformanceCounter 監測、安裝類別與部署指令,即可建立穩健的 Windows Service。新專案強烈建議採用 Generic Host (Worker Service),享受現代化 DI、設定、日誌生態,並保留未來跨平台遷移彈性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved