Compare commits
3 Commits
26d04daceb
...
4be0fdcb74
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4be0fdcb74 | ||
|
|
93164a86d8 | ||
|
|
0ff1014e62 |
11
src-tauri/Cargo.lock
generated
11
src-tauri/Cargo.lock
generated
@@ -872,15 +872,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs_io"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.0"
|
||||
@@ -3718,8 +3709,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"csv",
|
||||
"encoding_rs",
|
||||
"encoding_rs_io",
|
||||
"minidump",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
[package]
|
||||
name = "system-doctor"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
description = "System diagnosis"
|
||||
authors = ["Julian"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
# The `_lib` suffix may seem redundant but it is necessary
|
||||
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||
name = "system_doctor_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
@@ -27,11 +20,7 @@ wmi = "0.13"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
minidump = "0.19"
|
||||
# [新增] 用于解析 BlueScreenView 导出的 CSV
|
||||
csv = "1.3"
|
||||
# [新增] 用于处理可能的文件编码问题
|
||||
encoding_rs = "0.8"
|
||||
encoding_rs_io = "0.1"
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![greet])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// src-tauri/src/main.rs
|
||||
|
||||
// 全局允许非标准命名风格
|
||||
#![allow(non_camel_case_types, non_snake_case)]
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
@@ -9,6 +7,7 @@ use sysinfo::{System, Disks};
|
||||
use wmi::{COMLibrary, WMIConnection};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
// [修复] 移除了未使用的 Write
|
||||
use std::io::Read;
|
||||
use std::process::Command;
|
||||
use tauri::Emitter;
|
||||
@@ -131,61 +130,112 @@ fn format_wmi_time(wmi_str: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
// [扩展] 翻译 BugCheck Code (u32) - 增加了更多常见代码
|
||||
fn translate_bugcheck_u32(code: u32) -> (String, String) {
|
||||
match code {
|
||||
// 驱动与内存相关
|
||||
0x0000000A => (
|
||||
"IRQL_NOT_LESS_OR_EQUAL (0x0A)".to_string(),
|
||||
"驱动程序使用了不正确的内存地址。通常由有缺陷的驱动程序(如杀毒软件、虚拟光驱驱动)或硬件兼容性问题引起。建议更新所有驱动程序。"
|
||||
.to_string(),
|
||||
),
|
||||
0x000000D1 => (
|
||||
"DRIVER_IRQL_NOT_LESS_OR_EQUAL (0xD1)".to_string(),
|
||||
"驱动程序使用了不正确的内存地址。通常是驱动程序冲突或损坏。请检查最近安装的硬件驱动(显卡、网卡等),尝试回滚或更新驱动。"
|
||||
"驱动程序尝试访问未分页内存。这是最常见的蓝屏之一,通常是驱动程序冲突或损坏。请检查最近安装的硬件驱动(显卡、网卡等)。"
|
||||
.to_string(),
|
||||
),
|
||||
0x0000007E | 0x1000007E => (
|
||||
"SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (0x7E)".to_string(),
|
||||
"系统线程抛出了未捕获的异常。可能是显卡驱动不兼容,或者BIOS设置问题。建议重装显卡驱动,或恢复BIOS默认设置。"
|
||||
"系统线程抛出了未捕获的异常。可能是显卡驱动不兼容,或者BIOS设置问题。建议重装显卡驱动,或更新 BIOS。"
|
||||
.to_string(),
|
||||
),
|
||||
0x0000001A => (
|
||||
"MEMORY_MANAGEMENT (0x1A)".to_string(),
|
||||
"严重的内存管理错误。高度怀疑内存条物理故障。建议立即运行 Windows 内存诊断工具,或者尝试拔插内存条。"
|
||||
.to_string(),
|
||||
),
|
||||
0x000000EF => (
|
||||
"CRITICAL_PROCESS_DIED (0xEF)".to_string(),
|
||||
"Windows 核心进程意外终止。通常是系统文件损坏或硬盘故障。建议运行 'sfc /scannow' 修复系统,并检查硬盘健康度。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000124 => (
|
||||
"WHEA_UNCORRECTABLE_ERROR (0x124)".to_string(),
|
||||
"硬件发生无法纠正的物理错误。这是纯硬件故障。通常是 CPU 电压不足、超频失败、过热,或者主板/PCIe设备故障。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000116 => (
|
||||
"VIDEO_TDR_FAILURE (0x116)".to_string(),
|
||||
"显卡响应超时。显卡驱动崩溃或显卡过热。如果你在玩游戏,可能是显卡超频不稳定或散热硅脂干了。"
|
||||
0x0000001E => (
|
||||
"KMODE_EXCEPTION_NOT_HANDLED (0x1E)".to_string(),
|
||||
"内核模式程序生成了处理器无法捕获的异常。通常是硬件兼容性问题或驱动程序错误。建议检查是否有新安装的硬件。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000050 => (
|
||||
"PAGE_FAULT_IN_NONPAGED_AREA (0x50)".to_string(),
|
||||
"试图访问无效的内存地址。可能是内存条故障,或者是防病毒软件/驱动程序冲突。建议检查内存。"
|
||||
"试图访问无效的内存地址。极大概率是内存条故障(接触不良或损坏),或者是防病毒软件/驱动程序冲突。建议运行内存诊断。"
|
||||
.to_string(),
|
||||
),
|
||||
0x0000000A => (
|
||||
"IRQL_NOT_LESS_OR_EQUAL (0x0A)".to_string(),
|
||||
"驱动程序使用了不正确的内存地址。通常由有缺陷的驱动程序或硬件兼容性问题引起。"
|
||||
0x0000001A => (
|
||||
"MEMORY_MANAGEMENT (0x1A)".to_string(),
|
||||
"严重的内存管理错误。高度怀疑内存条物理故障。建议立即运行 Windows 内存诊断工具,或者尝试拔插/更换内存条。"
|
||||
.to_string(),
|
||||
),
|
||||
0x0000003B => (
|
||||
"SYSTEM_SERVICE_EXCEPTION (0x3B)".to_string(),
|
||||
"系统服务执行异常。通常与图形驱动程序或过时的系统文件有关。"
|
||||
|
||||
// 硬件与系统核心相关
|
||||
0x00000124 => (
|
||||
"WHEA_UNCORRECTABLE_ERROR (0x124)".to_string(),
|
||||
"硬件发生无法纠正的物理错误。这是纯硬件故障。通常是 CPU 电压不足(缩缸)、超频失败、过热,或者主板/PCIe设备故障。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000101 => (
|
||||
"CLOCK_WATCHDOG_TIMEOUT (0x101)".to_string(),
|
||||
"CPU 核心死锁。系统检测到某个 CPU 核心在规定时间内没有响应。通常是因为 CPU 超频不稳定、电压不足或过热。"
|
||||
.to_string(),
|
||||
),
|
||||
0x000000EF => (
|
||||
"CRITICAL_PROCESS_DIED (0xEF)".to_string(),
|
||||
"Windows 核心进程意外终止。通常是系统文件严重损坏或硬盘出现坏道导致无法读取系统文件。建议运行 'sfc /scannow'。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000133 => (
|
||||
"DPC_WATCHDOG_VIOLATION (0x133)".to_string(),
|
||||
"DPC 看门狗超时。通常是 SSD 固件过旧或无线网卡驱动冲突导致系统卡死时间过长。"
|
||||
"DPC 看门狗超时。系统被某个驱动程序(通常是 SSD 固件、SATA 驱动或无线网卡驱动)卡死太久。建议更新 SSD 固件和主板芯片组驱动。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000139 => (
|
||||
"KERNEL_SECURITY_CHECK_FAILURE (0x139)".to_string(),
|
||||
"内核检测到关键数据结构损坏。通常由内存错误、驱动程序 Bug 或病毒导致。建议检查内存和更新驱动。"
|
||||
.to_string(),
|
||||
),
|
||||
|
||||
// 显卡与电源相关
|
||||
0x00000116 => (
|
||||
"VIDEO_TDR_FAILURE (0x116)".to_string(),
|
||||
"显卡响应超时。显卡驱动崩溃且无法恢复。如果你在玩游戏,可能是显卡超频不稳定、电源功率不足或散热硅脂干了。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000119 => (
|
||||
"VIDEO_SCHEDULER_INTERNAL_ERROR (0x119)".to_string(),
|
||||
"显卡调度器内部错误。这表明显卡驱动发送了无效数据,或者是显卡显存(VRAM)出现故障。"
|
||||
.to_string(),
|
||||
),
|
||||
0x0000009F => (
|
||||
"DRIVER_POWER_STATE_FAILURE (0x9F)".to_string(),
|
||||
"驱动程序电源状态故障。通常发生在电脑休眠、睡眠或唤醒时。某个设备的驱动程序没有正确响应电源指令。建议检查电源管理设置。"
|
||||
.to_string(),
|
||||
),
|
||||
|
||||
// 存储与启动相关
|
||||
0x0000007B => (
|
||||
"INACCESSIBLE_BOOT_DEVICE (0x7B)".to_string(),
|
||||
"无法访问启动设备。通常是 BIOS 中的硬盘模式设置错误(如 AHCI/RAID 切换)或引导扇区损坏。也可能是硬盘物理连接松动。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000024 => (
|
||||
"NTFS_FILE_SYSTEM (0x24)".to_string(),
|
||||
"NTFS 文件系统损坏。这通常意味着硬盘出现坏道或文件系统逻辑错误。建议立即运行 'chkdsk /f /r' 修复磁盘。"
|
||||
.to_string(),
|
||||
),
|
||||
0x00000154 => (
|
||||
"UNEXPECTED_STORE_EXCEPTION (0x154)".to_string(),
|
||||
"系统存储组件检测到意外异常。通常指向系统盘(SSD/HDD)出现故障或连接线松动,导致系统无法读取关键数据。"
|
||||
.to_string(),
|
||||
),
|
||||
|
||||
// 系统服务异常
|
||||
0x0000003B => (
|
||||
"SYSTEM_SERVICE_EXCEPTION (0x3B)".to_string(),
|
||||
"系统服务执行异常。通常与图形驱动程序、系统文件损坏或过时的驱动有关。"
|
||||
.to_string(),
|
||||
),
|
||||
_ => (
|
||||
format!("未知错误代码: 0x{:X}", code),
|
||||
"建议手动搜索此错误代码。通用建议:更新驱动、检查内存、扫描病毒。".to_string(),
|
||||
"此代码较为罕见,建议手动在搜索引擎中搜索此错误代码。通用排查步骤:1. 更新所有驱动;2. 运行内存诊断;3. 检查系统文件完整性。"
|
||||
.to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -231,34 +281,61 @@ where T: Deref<Target = [u8]>
|
||||
})
|
||||
}
|
||||
|
||||
// BlueScreenView 外部调用分析
|
||||
// [修复] BlueScreenView 外部调用分析 (使用有损 UTF-8 转换)
|
||||
fn analyze_with_bluescreenview(dump_path: &Path) -> Result<BsodAnalysisReport, String> {
|
||||
let bsv_exe = "BlueScreenView.exe";
|
||||
|
||||
let mut temp_csv_path = std::env::temp_dir();
|
||||
temp_csv_path.push(format!("bsod_report_{}.csv", chrono::Utc::now().timestamp_millis()));
|
||||
|
||||
// BlueScreenView.exe /LoadFrom 3 /SingleDumpFile <DumpFilePath> /scomma <OutFile>
|
||||
let status = Command::new(bsv_exe)
|
||||
.arg("/LoadFrom")
|
||||
.arg("3")
|
||||
.arg("/SingleDumpFile")
|
||||
.arg(dump_path.to_string_lossy().to_string())
|
||||
.arg("/scomma")
|
||||
.arg(temp_csv_path.to_string_lossy().to_string())
|
||||
.status();
|
||||
|
||||
if status.is_err() {
|
||||
return Err("未找到 BlueScreenView.exe,无法分析内核转储文件。请将 BlueScreenView.exe 放入程序目录。".to_string());
|
||||
return Err("未找到 BlueScreenView.exe,或没有以管理员运行。".to_string());
|
||||
}
|
||||
|
||||
// [修改] 使用 fs::read 读取原始字节,然后有损转换为 String
|
||||
// 这样即使文件是 UTF-16 或者包含非法 UTF-8 字符,也不会 panic
|
||||
let content_bytes = fs::read(&temp_csv_path).map_err(|_| "BlueScreenView 分析未生成有效数据。".to_string())?;
|
||||
let _ = fs::remove_file(temp_csv_path);
|
||||
let content = String::from_utf8_lossy(&content_bytes);
|
||||
|
||||
// 1. 简单判断:如果包含很多 0 字节,可能是 UTF-16,尝试手动过滤 0 (简单粗暴但对 CSV 有效)
|
||||
// 2. 或者直接 lossy 转换,虽然 UTF-16 会乱码,但 CSV crate 有时能容忍乱码分隔符
|
||||
// 最稳妥的方式其实是先尝试 UTF-16LE 转换逻辑(但我们刚才移除了 encoding_rs),
|
||||
// 所以这里我们用一个简单的技巧:如果文件头两个字节是 FF FE,手动跳过 BOM
|
||||
|
||||
let content_string = if content_bytes.len() > 2 && content_bytes[0] == 0xFF && content_bytes[1] == 0xFE {
|
||||
// 简易 UTF-16LE 转 UTF-8 (只针对 ASCII 字符有效,对中文可能会乱码,但在这个场景下够用了)
|
||||
// BlueScreenView 的列名和关键 hex 代码都是 ASCII
|
||||
let mut s = String::new();
|
||||
for i in (2..content_bytes.len()).step_by(2) {
|
||||
if i+1 < content_bytes.len() {
|
||||
// 取低位字节,忽略高位 0x00
|
||||
let c = content_bytes[i] as char;
|
||||
s.push(c);
|
||||
}
|
||||
}
|
||||
s
|
||||
} else {
|
||||
String::from_utf8_lossy(&content_bytes).to_string()
|
||||
};
|
||||
|
||||
let mut rdr = csv::ReaderBuilder::new()
|
||||
.has_headers(false)
|
||||
.from_reader(content.as_bytes());
|
||||
.from_reader(content_string.as_bytes());
|
||||
|
||||
for result in rdr.records() {
|
||||
if let Ok(record) = result {
|
||||
if record.len() > 15 {
|
||||
// 跳过 Header 行
|
||||
if &record[0] == "Dump File" { continue; }
|
||||
|
||||
let bug_check_string = &record[2];
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "system-doctor",
|
||||
"title": "系统检查 v0.1",
|
||||
"width": 1200,
|
||||
"height": 800
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
:class="{ active: currentTab === 'overview' }"
|
||||
@click="currentTab = 'overview'"
|
||||
>
|
||||
<span class="nav-icon">📊</span> 静态概览
|
||||
<span class="nav-icon">📊</span> 健康概览
|
||||
</button>
|
||||
<button
|
||||
class="nav-item"
|
||||
@@ -30,7 +30,7 @@
|
||||
</button>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">Pro v1.2</span>
|
||||
<span class="version">Generated by AI</span>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user