使用 Rust 在 Windows 系统实现一个服务

分类:Rust     发布时间:2025-03-24     最后更新:2025-03-27     浏览数:119
文章详细讲述了如何使用 Rust 实现了一个 Windows 的服务用来监控特定文件夹内新增文件事件。

最近我的 C 盘由蓝色变为红色(磁盘可用空间只有15GB),心里很郁闷,我最近没有安装新软件,磁盘为什么就会满,经过了一番排查后,发现有一个软件会偷偷截屏并存为图片文件,而且几乎每隔两分钟就截屏一次,非常恶心。

为了解决这个问题:

1、需要监控某个文件夹,如果文件夹里面新增了文件,则删除。

2、服务需要运行在后台。

3、服务需要开机自启动。

恰好我最近在学 Rust,所以我就使用 Rust 来练练手。

监控文件夹

// 监控的文件夹路径
let folder_to_watch = Path::new("D:\\test");

// 创建一个通道用于接收文件系统事件
let (tx, rx) = channel();

// 创建一个文件监控器
let mut watcher = RecommendedWatcher::new(tx, NotifyConfig::default()).expect("无法创建文件监控器");

// 监听指定文件夹的更改事件
watcher.watch(folder_to_watch, RecursiveMode::NonRecursive).expect("无法监听文件夹");

// 进入事件监听循环
loop {
    match rx.recv() {
        Ok(event) => match event {
            Ok(event) => {
                if event.kind == EventKind::Create(CreateKind::Any) {
                    for path in event.paths {
                        remove_file(&path);
                    }
                }
            }
            Err(e) => error!("错误: {}", e),
        },
        Err(e) => error!("监控文件错误: {}", e),
    }
}

fn remove_file(path: &PathBuf) {
    if path.is_file() {
        // 尝试删除文件
        sleep(Duration::from_secs(1));
        match fs::remove_file(&path) {
            Ok(_) => info!("成功删除文件: {:?}", path),
            Err(e) => {
                error!("删除文件失败: {:?}, 错误: {}", path, e)
            }
        }
    }
}

info!error! 宏来自 log crate。

实现服务

实现服务依赖了 windows-service

服务的主体结构是这样的:

use log::error;
use std::ffi::OsString;
use windows_service::service_dispatcher;

#[macro_use]
extern crate windows_service;
define_windows_service!(ffi_service_main, my_service_main);

const SERVICE_NAME: &str = "FolderMonitor";

fn my_service_main(arguments: Vec<OsString>) {
    if let Err(e) = run_service(arguments) {
        error!("运行服务错误:{:?}", e);
    }
}

fn run_service(_args: Vec<OsString>) -> Result<(), windows_service::Error> {
    // 服务入口
    // @TODO 监控文件夹
    // @TODO 服务运行时,把服务状态改为运行中
    // @TODO 服务停止运行时,退出事件监听循环、停止监控文件夹、把服务状态改为已停止
    Ok(())
}

fn main() -> Result<(), windows_service::Error> {
    service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
    Ok(())
}

完整的run_service代码如下

fn run_service(_args: Vec<OsString>) -> Result<(), windows_service::Error> {
    // 初始化日志系统,将日志写入 LOG_FILE
    let log_file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(LOG_FILE)
        .expect("无法打开日志文件");

    // 自定义日志格式:包含日期和时间
    let config = ConfigBuilder::new().set_time_format_rfc3339().build();

    CombinedLogger::init(vec![
        TermLogger::new(
            LevelFilter::Warn,
            Config::default(),
            TerminalMode::Mixed,
            ColorChoice::Auto,
        ),
        WriteLogger::new(LevelFilter::Info, config, log_file),
    ])
    .unwrap();

    info!("Windows 服务已启动");

    // 用于控制主循环的退出
    let running = Arc::new(AtomicBool::new(true));
    let running_clone = Arc::clone(&running);

    // 监控的文件夹路径
    let folder_to_watch = Path::new(MONITOR_PATH);

    // 创建一个通道用于接收文件系统事件
    let (tx, rx) = channel();

    // 创建一个文件监控器
    let mut watcher =
        RecommendedWatcher::new(tx, NotifyConfig::default()).expect("无法创建文件监控器");

    // 监听指定文件夹的更改事件
    watcher
        .watch(folder_to_watch, RecursiveMode::NonRecursive)
        .expect("无法监听文件夹");

    // 设定 Windows 服务控制处理器
    let status_handle =
        service_control_handler::register(SERVICE_NAME, move |control| match control {
            ServiceControl::Stop => {
                info!("收到服务停止命令");
                running_clone.store(false, Ordering::Relaxed); // 更新状态,让主循环退出
                return ServiceControlHandlerResult::NoError;
            }
            ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
            _ => ServiceControlHandlerResult::NotImplemented,
        })
        .unwrap();

    status_handle
        .set_service_status(ServiceStatus {
            service_type: ServiceType::OWN_PROCESS,
            current_state: ServiceState::Running,
            controls_accepted: ServiceControlAccept::STOP,
            exit_code: ServiceExitCode::Win32(0),
            checkpoint: 0,
            wait_hint: Duration::default(),
            process_id: None,
        })
        .unwrap();

    info!("开始监控文件夹: {}", folder_to_watch.to_string_lossy());

    // 进入事件监听循环
    while running.load(Ordering::Relaxed) {
        match rx.recv_timeout(Duration::from_secs(1)) {
            Ok(event) => match event {
                Ok(event) => {
                    if event.kind == EventKind::Create(CreateKind::Any) {
                        for path in event.paths {
                            remove_file(&path);
                        }
                    }
                }
                Err(e) => error!("错误: {}", e),
            },
            Err(_) => {}
        }
    }

    // 停止监听文件夹变动
    if let Err(e) = watcher.unwatch(folder_to_watch) {
        error!("停止监控文件夹失败: {}", e);
    } else {
        info!("已停止监控文件夹: {}", folder_to_watch.to_string_lossy());
    }

    status_handle
        .set_service_status(ServiceStatus {
            service_type: ServiceType::OWN_PROCESS,
            current_state: ServiceState::Stopped,
            controls_accepted: ServiceControlAccept::empty(),
            exit_code: ServiceExitCode::Win32(0),
            checkpoint: 0,
            wait_hint: Duration::default(),
            process_id: None,
        })
        .unwrap();

    info!("服务已停止");

    Ok(())
}

安装服务

以管理员身份运行命令行提示符应用

sc create FolderMonitor binPath= "D:\Program Files\folder-monitor\folder_monitor_service.exe" start= auto # 创建服务并设置为自动启动

sc start FolderMonitor # 启动服务

sc stop FolderMonitor # 停止服务

参考

注:以上研究成果是在ChatGPT帮助下完成。

notify

windows-service

log

simplelog

上一篇: 浅析 Rust 中的模块系统 下一篇: 没有了