event_tracing_windows 0.0.1
event_tracing_windows: ^0.0.1 copied to clipboard
A Flutter plugin for Windows that uses ETW (Event Tracing for Windows) to monitor process and file system activities in real-time.
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:event_tracing_windows/event_tracing_windows.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Windows ETW 监控',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _plugin = EventTracingWindows();
bool _isProcessMonitoringActive = false;
bool _isFileMonitoringActive = false;
final List<ProcessEvent> _processEvents = [];
final List<FileEvent> _fileEvents = [];
StreamSubscription<ProcessEvent>? _processSubscription;
StreamSubscription<FileEvent>? _fileSubscription;
final int _maxEvents = 100; // 最多保留的事件数量
Future<void> _toggleProcessMonitoring() async {
if (_isProcessMonitoringActive) {
// 停止监听
await _plugin.stopProcessMonitoring();
await _processSubscription?.cancel();
_processSubscription = null;
setState(() {
_isProcessMonitoringActive = false;
});
} else {
// 开始监听
final success = await _plugin.startProcessMonitoring();
if (success) {
_processSubscription = _plugin.processEventStream.listen((event) {
setState(() {
_processEvents.insert(0, event);
if (_processEvents.length > _maxEvents) {
_processEvents.removeLast();
}
});
});
setState(() {
_isProcessMonitoringActive = true;
});
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('进程监控已启动')));
} else {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('启动进程监控失败,请确保以管理员权限运行'),
backgroundColor: Colors.red,
),
);
}
}
}
Future<void> _toggleFileMonitoring() async {
if (_isFileMonitoringActive) {
// 停止监听
await _plugin.stopFileMonitoring();
await _fileSubscription?.cancel();
_fileSubscription = null;
setState(() {
_isFileMonitoringActive = false;
});
} else {
// 开始监听
final success = await _plugin.startFileMonitoring();
if (success) {
_fileSubscription = _plugin.fileEventStream.listen((event) {
setState(() {
_fileEvents.insert(0, event);
if (_fileEvents.length > _maxEvents) {
_fileEvents.removeLast();
}
});
});
setState(() {
_isFileMonitoringActive = true;
});
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('文件监控已启动')));
} else {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('启动文件监控失败,请确保以管理员权限运行'),
backgroundColor: Colors.red,
),
);
}
}
}
void _clearProcessEvents() {
setState(() {
_processEvents.clear();
});
}
void _clearFileEvents() {
setState(() {
_fileEvents.clear();
});
}
@override
void dispose() {
_processSubscription?.cancel();
_fileSubscription?.cancel();
_plugin.stopProcessMonitoring();
_plugin.stopFileMonitoring();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: const Text('Windows ETW 事件追踪'),
bottom: const TabBar(
tabs: [
Tab(icon: Icon(Icons.apps), text: '进程监控'),
Tab(icon: Icon(Icons.folder), text: '文件监控'),
],
),
),
body: TabBarView(
children: [_buildProcessMonitorTab(), _buildFileMonitorTab()],
),
),
);
}
Widget _buildProcessMonitorTab() {
return Column(
children: [
_buildControlPanel(
isActive: _isProcessMonitoringActive,
onToggle: _toggleProcessMonitoring,
onClear: _clearProcessEvents,
title: '进程监控',
eventCount: _processEvents.length,
),
Expanded(
child: _processEvents.isEmpty
? const Center(
child: Text(
'暂无事件\n点击上方按钮开始监控',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey, fontSize: 16),
),
)
: ListView.builder(
itemCount: _processEvents.length,
itemBuilder: (context, index) {
final event = _processEvents[index];
return _buildProcessEventItem(event);
},
),
),
],
);
}
Widget _buildFileMonitorTab() {
return Column(
children: [
_buildControlPanel(
isActive: _isFileMonitoringActive,
onToggle: _toggleFileMonitoring,
onClear: _clearFileEvents,
title: '文件监控',
eventCount: _fileEvents.length,
),
Expanded(
child: _fileEvents.isEmpty
? const Center(
child: Text(
'暂无事件\n点击上方按钮开始监控',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey, fontSize: 16),
),
)
: ListView.builder(
itemCount: _fileEvents.length,
itemBuilder: (context, index) {
final event = _fileEvents[index];
return _buildFileEventItem(event);
},
),
),
],
);
}
Widget _buildControlPanel({
required bool isActive,
required VoidCallback onToggle,
required VoidCallback onClear,
required String title,
required int eventCount,
}) {
return Container(
padding: const EdgeInsets.all(16),
color: Colors.grey[100],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'状态: ${isActive ? "运行中" : "已停止"} | 事件数: $eventCount',
style: TextStyle(color: Colors.grey[600], fontSize: 12),
),
],
),
),
const SizedBox(width: 8),
ElevatedButton.icon(
onPressed: onToggle,
icon: Icon(isActive ? Icons.stop : Icons.play_arrow),
label: Text(isActive ? '停止' : '开始'),
style: ElevatedButton.styleFrom(
backgroundColor: isActive ? Colors.red : Colors.green,
foregroundColor: Colors.white,
),
),
const SizedBox(width: 8),
IconButton(
onPressed: onClear,
icon: const Icon(Icons.delete_outline),
tooltip: '清除记录',
),
],
),
if (!isActive)
const Padding(
padding: EdgeInsets.only(top: 8),
child: Text(
'⚠️ 需要管理员权限才能启动监控',
style: TextStyle(color: Colors.orange, fontSize: 12),
),
),
],
),
);
}
Widget _buildProcessEventItem(ProcessEvent event) {
final dateFormat = DateFormat('HH:mm:ss.SSS');
final time = DateTime.fromMillisecondsSinceEpoch(
event.timestamp ~/ 10000, // Windows FILETIME 转换为毫秒
isUtc: true,
).toLocal();
final isStart = event.type == ProcessEventType.started;
return Card(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: ListTile(
leading: CircleAvatar(
backgroundColor: isStart ? Colors.green : Colors.red,
child: Icon(
isStart ? Icons.play_arrow : Icons.stop,
color: Colors.white,
),
),
title: Text(
event.executablePath,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('PID: ${event.processId}'),
Text('父进程 ID: ${event.parentProcessId}'),
Text(
dateFormat.format(time),
style: TextStyle(fontSize: 10, color: Colors.grey[500]),
),
],
),
trailing: Chip(
label: Text(
isStart ? '启动' : '终止',
style: const TextStyle(fontSize: 11),
),
backgroundColor: isStart ? Colors.green[100] : Colors.red[100],
),
),
);
}
Widget _buildFileEventItem(FileEvent event) {
final dateFormat = DateFormat('HH:mm:ss.SSS');
final time = DateTime.fromMillisecondsSinceEpoch(
event.timestamp ~/ 10000, // Windows FILETIME 转换为毫秒
isUtc: true,
).toLocal();
final typeInfo = _getFileEventTypeInfo(event.type);
return Card(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: ListTile(
leading: CircleAvatar(
backgroundColor: typeInfo.$2,
child: Icon(typeInfo.$1, color: Colors.white, size: 20),
),
title: Text(
event.filePath.split('\\').last,
style: const TextStyle(fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
event.filePath,
style: TextStyle(fontSize: 11, color: Colors.grey[600]),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text('PID: ${event.processId}'),
Text(
dateFormat.format(time),
style: TextStyle(fontSize: 10, color: Colors.grey[500]),
),
],
),
trailing: Chip(
label: Text(typeInfo.$3, style: const TextStyle(fontSize: 11)),
backgroundColor: typeInfo.$2.withOpacity(0.2),
),
),
);
}
(IconData, Color, String) _getFileEventTypeInfo(FileEventType type) {
switch (type) {
case FileEventType.created:
return (Icons.add_circle, Colors.green, '创建');
case FileEventType.deleted:
return (Icons.delete, Colors.red, '删除');
case FileEventType.modified:
return (Icons.edit, Colors.blue, '修改');
case FileEventType.renamed:
return (Icons.drive_file_rename_outline, Colors.orange, '重命名');
}
}
}