<p>最近我的 C 盘由蓝色变为红色(磁盘可用空间只有15GB),心里很郁闷,我最近没有安装新软件,磁盘为什么就会满,经过了一番排查后,发现有一个软件会偷偷截屏并存为图片文件,而且几乎每隔两分钟就截屏一次,非常恶心。</p>
<p>为了解决这个问题:</p>
<p>1、需要监控某个文件夹,如果文件夹里面新增了文件,则删除。</p>
<p>2、服务需要运行在后台。</p>
<p>3、服务需要开机自启动。</p>
<p>恰好我最近在学 Rust,所以我就使用 Rust 来练练手。</p>
<h1>监控文件夹</h1>
<pre><code class="language-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)
}
}
}
}</code></pre>
<p><code>info!</code> 和 <code>error!</code> 宏来自 <code>log</code> crate。</p>
<h1>实现服务</h1>
<p>实现服务依赖了 <a href="https://crates.io/crates/windows-service">windows-service</a></p>
<p>服务的主体结构是这样的:</p>
<pre><code class="language-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(())
}</code></pre>
<p>完整的<code>run_service</code>代码如下</p>
<pre><code class="language-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(())
}</code></pre>
<h1>安装服务</h1>
<p>以管理员身份运行<code>命令行提示符</code>应用</p>
<pre><code class="language-cmd">sc create FolderMonitor binPath= "D:\Program Files\folder-monitor\folder_monitor_service.exe" start= auto # 创建服务并设置为自动启动
sc start FolderMonitor # 启动服务
sc stop FolderMonitor # 停止服务</code></pre>
<h1>参考</h1>
<p>注:以上研究成果是在<code>ChatGPT</code>帮助下完成。</p>
<p><a href="https://crates.io/crates/notify">notify</a></p>
<p><a href="https://crates.io/crates/windows-service">windows-service</a></p>
<p><a href="https://crates.io/crates/log">log</a></p>
<p><a href="https://crates.io/crates/simplelog">simplelog</a></p>
详情