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

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

为了解决这个问题:

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

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

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

恰好我最近在学 Rust,所以我就使用 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

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

rust 复制代码
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代码如下

rust 复制代码
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(())
}

安装服务

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

cmd 复制代码
sc create FolderMonitor binPath= "D:\Program Files\folder-monitor\folder_monitor_service.exe" # 创建服务

sc start FolderMonitor # 启动服务

sc stop FolderMonitor # 停止服务

参考

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

notify

windows-service

log

simplelog

相关推荐
m0_480502645 小时前
Rust 入门 注释和文档之 cargo doc (二十三)
开发语言·后端·rust
盒马盒马13 小时前
Rust:变量、常量与数据类型
开发语言·rust
傻啦嘿哟13 小时前
Rust爬虫实战:用reqwest+select打造高效网页抓取工具
开发语言·爬虫·rust
咸甜适中1 天前
rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(十四)垂直滚动条
笔记·学习·rust·egui
张志鹏PHP全栈1 天前
Rust第四天,Rust中常见编程概念
后端·rust
咸甜适中1 天前
rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(十五)网格布局
笔记·学习·rust·egui
susnm2 天前
最后的最后
rust·全栈
bruce541103 天前
深入理解 Rust Axum:两种依赖注入模式的实践与对比(二)
rust
该用户已不存在4 天前
这几款Rust工具,开发体验直线上升
前端·后端·rust
m0_480502646 天前
Rust 入门 生命周期-next2 (十九)
开发语言·后端·rust