diff --git a/LiteMonitor.csproj b/LiteMonitor.csproj
index 9241a9b..093db9a 100644
--- a/LiteMonitor.csproj
+++ b/LiteMonitor.csproj
@@ -9,6 +9,11 @@
x64
resources\assets\app.ico
app.manifest
+ true
+ true
+ false
+
+ true
none
false
@@ -18,11 +23,18 @@
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Properties/Resources.resx b/Properties/Resources.resx
index c6835ff..50322d5 100644
--- a/Properties/Resources.resx
+++ b/Properties/Resources.resx
@@ -119,30 +119,30 @@
- ..\resources\assets\app.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ ..\resources\assets\app.ico;System.Drawing.Icon, System.Drawing.Common
- ..\resources\assets\CleanMem.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ ..\resources\assets\CleanMem.png;System.Drawing.Bitmap, System.Drawing.Common
CleanMem
- ..\resources\assets\HardwareInfo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ ..\resources\assets\HardwareInfo.png;System.Drawing.Bitmap, System.Drawing.Common
HardwareInfo.png
- ..\resources\assets\NetworkTest.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ ..\resources\assets\NetworkTest.png;System.Drawing.Bitmap, System.Drawing.Common
网络图标
- ..\resources\assets\Settings.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ ..\resources\assets\Settings.png;System.Drawing.Bitmap, System.Drawing.Common
Settings
- ..\resources\assets\ThemeIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ ..\resources\assets\ThemeIcon.png;System.Drawing.Bitmap, System.Drawing.Common
主题图标
- ..\resources\assets\Traffic.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ ..\resources\assets\Traffic.png;System.Drawing.Bitmap, System.Drawing.Common
历史流量统计
-
\ No newline at end of file
+
diff --git a/build/LiteMonitor.iss b/build/LiteMonitor.iss
new file mode 100644
index 0000000..8baf2c5
--- /dev/null
+++ b/build/LiteMonitor.iss
@@ -0,0 +1,41 @@
+#define MyAppName "LiteMonitor"
+#define MyAppVersion "1.3.4"
+#define MyAppPublisher "Diorser"
+#define MyAppExeName "LiteMonitor.exe"
+
+[Setup]
+AppId={{8A6A4E34-36AF-430E-B42E-54793F51DE79}
+AppName={#MyAppName}
+AppVersion={#MyAppVersion}
+AppPublisher={#MyAppPublisher}
+DefaultDirName={autopf}\{#MyAppName}
+DefaultGroupName={#MyAppName}
+DisableProgramGroupPage=yes
+PrivilegesRequired=admin
+ArchitecturesAllowed=x64compatible
+ArchitecturesInstallIn64BitMode=x64compatible
+OutputDir=..\artifacts\installer
+OutputBaseFilename=LiteMonitor_Setup_{#MyAppVersion}_x64
+SetupIconFile=..\resources\assets\app.ico
+Compression=lzma2/ultra64
+SolidCompression=yes
+WizardStyle=modern
+UninstallDisplayIcon={app}\{#MyAppExeName}
+CloseApplications=yes
+
+[Languages]
+Name: "chinesesimp"; MessagesFile: "compiler:Default.isl"
+
+[Tasks]
+Name: "desktopicon"; Description: "创建桌面快捷方式"; GroupDescription: "附加任务:"; Flags: unchecked
+
+[Files]
+Source: "..\artifacts\publish-stable\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+
+[Icons]
+Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
+Name: "{group}\卸载 {#MyAppName}"; Filename: "{uninstallexe}"
+Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
+
+[Run]
+Filename: "{app}\{#MyAppExeName}"; Description: "启动 {#MyAppName}"; Flags: nowait postinstall skipifsilent
diff --git a/resources/lang/en.json b/resources/lang/en.json
index 5dd9fb5..4499f66 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -148,6 +148,9 @@
"TaskbarHoverShowAll": "Hover Details",
"TaskbarStyle": "Style",
"TaskbarMonitor": "Monitor",
+ "TaskbarMonitorSettings": "Screen Selection",
+ "TaskbarMonitorTip": "Choose which screens should show the taskbar widget. Auto mode follows the primary taskbar only.",
+ "TaskbarAllScreens": "Show On All Screens",
"TaskbarStyleBold": "Big",
"TaskbarStyleRegular": "Small",
"TaskbarAlign": "Position",
@@ -240,4 +243,4 @@
"PluginAddTarget": "+ Add Target",
"PluginDeleteConfirm": "Delete this plugin copy?"
}
-}
\ No newline at end of file
+}
diff --git a/resources/lang/zh-tw.json b/resources/lang/zh-tw.json
index e22fff5..65a58b9 100644
--- a/resources/lang/zh-tw.json
+++ b/resources/lang/zh-tw.json
@@ -145,6 +145,9 @@
"TaskbarHoverShowAll": "滑鼠懸停顯示詳情",
"TaskbarStyle": "工作列樣式",
"TaskbarMonitor": "顯示器螢幕",
+ "TaskbarMonitorSettings": "螢幕選擇",
+ "TaskbarMonitorTip": "可勾選工作列顯示在哪些螢幕上;勾選「自動模式」時,將只跟隨主螢幕工作列。",
+ "TaskbarAllScreens": "所有螢幕都顯示",
"TaskbarStyleBold": "大字模式",
"TaskbarStyleRegular": "小字模式",
"TaskbarAlign": "顯示位置",
@@ -238,4 +241,4 @@
"PluginAddTarget": "+ 新增監控目標",
"PluginDeleteConfirm": "確定要刪除此插件副本嗎?"
}
-}
\ No newline at end of file
+}
diff --git a/resources/lang/zh.json b/resources/lang/zh.json
index cc3abe1..ccaab56 100644
--- a/resources/lang/zh.json
+++ b/resources/lang/zh.json
@@ -148,6 +148,9 @@
"TaskbarHoverShowAll": "鼠标悬停显示详情",
"TaskbarStyle": "任务栏样式",
"TaskbarMonitor": "显示器屏幕",
+ "TaskbarMonitorSettings": "显示屏选择",
+ "TaskbarMonitorTip": "可勾选任务栏显示在哪些屏幕上;勾选“自动模式”时,将只跟随主屏任务栏。",
+ "TaskbarAllScreens": "所有屏幕都显示",
"TaskbarStyleBold": "大字模式",
"TaskbarStyleRegular": "小字模式",
"TaskbarAlign": "显示位置",
@@ -240,4 +243,4 @@
"PluginAddTarget": "+ 添加新监控目标",
"PluginDeleteConfirm": "确定要删除此插件副本吗?"
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/Actions/SettingsChanger.cs b/src/Core/Actions/SettingsChanger.cs
index dcde593..14eb521 100644
--- a/src/Core/Actions/SettingsChanger.cs
+++ b/src/Core/Actions/SettingsChanger.cs
@@ -57,6 +57,11 @@ public static void Merge(Settings live, Settings draft)
live.PluginInstances = System.Text.Json.JsonSerializer.Deserialize>(json) ?? new List();
continue;
}
+ if (p.Name == "TaskbarMonitorDevices")
+ {
+ live.TaskbarMonitorDevices = new List(draft.TaskbarMonitorDevices ?? new List());
+ continue;
+ }
if (p.Name == "Thresholds")
{
// 阈值是 Class 类型 (ThresholdsSet),必须深拷贝
diff --git a/src/Core/Settings.cs b/src/Core/Settings.cs
index a21252d..b3eccf1 100644
--- a/src/Core/Settings.cs
+++ b/src/Core/Settings.cs
@@ -85,6 +85,8 @@ public class Settings
// ★★★ 新增:指定任务栏显示的屏幕设备名 ("" = 自动/主屏) ★★★
public string TaskbarMonitorDevice { get; set; } = "";
+ public List TaskbarMonitorDevices { get; set; } = new List();
+ public bool TaskbarShowOnAllScreens { get; set; } = false;
// 任务栏行为配置
public bool TaskbarClickThrough { get; set; } = false; // 鼠标穿透
diff --git a/src/System/HardwareMonitor.cs b/src/System/HardwareMonitor.cs
index bf27da6..9301ccb 100644
--- a/src/System/HardwareMonitor.cs
+++ b/src/System/HardwareMonitor.cs
@@ -301,7 +301,7 @@ private void InitializeAsync()
_perfCounterManager.InitializeAsync();
// 这句耗时 4-5 秒,但在执行过程中,硬件会陆续添加到 _computer.Hardware
- _computer.Open();
+ OpenComputerSafe();
// ★★★ T0+级修复:彻底禁用历史记录,解决 SensorValue[] 飙升 ★★★
// 必须在 Open() 之后调用,此时传感器才被创建
@@ -442,7 +442,7 @@ private void ReloadComputerSafe()
_computer.Hardware.Clear();
}
- _computer.Open();
+ OpenComputerSafe();
DisableSensorHistory();
}
@@ -467,6 +467,44 @@ private void DisableSensorHistory()
catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[MemoryFix] Failed: {ex.Message}"); }
}
+ private void OpenComputerSafe()
+ {
+ try
+ {
+ _computer.Open();
+ }
+ catch (ArgumentNullException ex) when (IsMutexBootstrapFailure(ex))
+ {
+ System.Diagnostics.Debug.WriteLine($"[HardwareMonitor] Computer.Open mutex bootstrap failed, retrying in compatibility mode: {ex.Message}");
+ OpenComputerWithoutMutex();
+ }
+ }
+
+ private void OpenComputerWithoutMutex()
+ {
+ var type = typeof(Computer);
+ var addGroups = type.GetMethod("AddGroups", BindingFlags.Instance | BindingFlags.NonPublic);
+ var openField = type.GetField("_open", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ if (addGroups == null || openField == null)
+ throw new MissingMemberException("LibreHardwareMonitor compatibility entry points were not found.");
+
+ bool isOpen = openField.GetValue(_computer) as bool? ?? false;
+ if (!isOpen)
+ {
+ addGroups.Invoke(_computer, null);
+ openField.SetValue(_computer, true);
+ }
+ }
+
+ private static bool IsMutexBootstrapFailure(ArgumentNullException ex)
+ {
+ if (!string.Equals(ex.ParamName, "identity", StringComparison.OrdinalIgnoreCase))
+ return false;
+
+ return ex.StackTrace?.Contains("LibreHardwareMonitor.Hardware.Mutexes", StringComparison.Ordinal) == true;
+ }
+
// 递归更新子硬件,确保 SuperIO 刷新
private void UpdateWithSubHardware(IHardware hw)
{
diff --git a/src/System/HardwareServices/HardwareRules.cs b/src/System/HardwareServices/HardwareRules.cs
index 97e2dfe..db96417 100644
--- a/src/System/HardwareServices/HardwareRules.cs
+++ b/src/System/HardwareServices/HardwareRules.cs
@@ -34,9 +34,11 @@ public static int GetHwPriority(IHardware hw)
// 3. AMD 显卡
if (hw.HardwareType == HardwareType.GpuAmd)
{
- // 通用名 "AMD Radeon(TM) Graphics" 通常是核显 -> 优先级 2
- if (name.Equals("AMD Radeon(TM) Graphics", StringComparison.OrdinalIgnoreCase)) return 2;
- // 其他具体型号 (如 RX 7900 XTX) 视为独显 -> 优先级 0
+ // AMD 核显常见命名:Radeon(TM) Graphics / 610M / 780M / Vega 等
+ // 这类设备应低于独显,避免覆盖独显的 GPU.Load 映射
+ if (IsAmdIntegratedGpu(name)) return 2;
+
+ // 其他具体型号 (如 RX 7900 XTX / RX 7600M) 视为独显 -> 优先级 0
return 0;
}
@@ -77,6 +79,37 @@ public static bool IsDiscreteArc(string name)
return Has(name, " A") || Has(name, " B") || Has(name, " Pro");
}
+ private static bool IsAmdIntegratedGpu(string name)
+ {
+ if (string.IsNullOrEmpty(name)) return false;
+
+ if (name.Equals("AMD Radeon(TM) Graphics", StringComparison.OrdinalIgnoreCase))
+ return true;
+
+ // 独显关键字:RX / PRO / FIREPRO 优先视作独显
+ if (Has(name, "rx") || Has(name, "pro") || Has(name, "firepro"))
+ return false;
+
+ // 常见核显命名关键字
+ if (Has(name, "vega")) return true;
+ if (Has(name, "radeon") && Has(name, "graphics")) return true;
+
+ if (Has(name, "radeon"))
+ {
+ // 典型 RDNA 核显尾缀(610M/660M/680M/760M/780M/880M/890M)
+ string[] igpuTokens = { "610m", "660m", "680m", "760m", "780m", "880m", "890m" };
+ foreach (var token in igpuTokens)
+ {
+ if (Has(name, token)) return true;
+ }
+
+ // 没有 RX/PRO/FIREPRO 且标记为 Radeon(TM),大概率为核显
+ if (Has(name, "radeon(tm)")) return true;
+ }
+
+ return false;
+ }
+
///
/// 判断是否应该使用共享内存 (Shared Memory)
///
diff --git a/src/System/HardwareServices/HardwareValueProvider.cs b/src/System/HardwareServices/HardwareValueProvider.cs
index 4dd9541..2f7570a 100644
--- a/src/System/HardwareServices/HardwareValueProvider.cs
+++ b/src/System/HardwareServices/HardwareValueProvider.cs
@@ -169,6 +169,131 @@ public void PreCacheAllSensors(SensorMap map)
return null;
}
+ private static bool IsGpuLoadSensorCandidate(string name)
+ {
+ bool include =
+ SensorMap.Has(name, "core") ||
+ SensorMap.Has(name, "d3d 3d") ||
+ SensorMap.Has(name, "3d") ||
+ SensorMap.Has(name, "graphics") ||
+ SensorMap.Has(name, "gpu") ||
+ SensorMap.Has(name, "render") ||
+ SensorMap.Has(name, "utilization") ||
+ SensorMap.Has(name, "global") ||
+ name.Equals("load", StringComparison.OrdinalIgnoreCase);
+
+ if (!include) return false;
+
+ bool exclude =
+ SensorMap.Has(name, "memory") ||
+ SensorMap.Has(name, "video") ||
+ SensorMap.Has(name, "encode") ||
+ SensorMap.Has(name, "decode") ||
+ SensorMap.Has(name, "copy") ||
+ SensorMap.Has(name, "bus") ||
+ SensorMap.Has(name, "pcie") ||
+ SensorMap.Has(name, "media");
+
+ return !exclude;
+ }
+
+ private static int ScoreGpuLoadSensor(ISensor sensor)
+ {
+ string name = sensor.Name ?? "";
+ if (!IsGpuLoadSensorCandidate(name)) return int.MinValue;
+
+ int score = 0;
+
+ if (SensorMap.Has(name, "core")) score += 60;
+ if (SensorMap.Has(name, "gpu")) score += 45;
+ if (SensorMap.Has(name, "graphics")) score += 40;
+ if (SensorMap.Has(name, "d3d 3d")) score += 35;
+ else if (SensorMap.Has(name, "3d")) score += 25;
+ if (SensorMap.Has(name, "utilization")) score += 20;
+ if (SensorMap.Has(name, "global")) score += 20;
+ if (SensorMap.Has(name, "render")) score += 15;
+ if (name.Equals("load", StringComparison.OrdinalIgnoreCase)) score += 10;
+ if (!SensorMap.Has(name, "d3d")) score += 5;
+
+ return score;
+ }
+
+ private ISensor? FindBestGpuLoadSensor()
+ {
+ var gpu = _sensorMap.CachedGpu;
+ if (gpu == null) return null;
+
+ ISensor? best = null;
+ int bestScore = int.MinValue;
+ float bestValue = float.MinValue;
+
+ foreach (var sensor in gpu.Sensors)
+ {
+ if (sensor.SensorType != SensorType.Load) continue;
+
+ int score = ScoreGpuLoadSensor(sensor);
+ if (score == int.MinValue) continue;
+
+ float value = sensor.Value ?? float.MinValue;
+
+ if (best == null ||
+ score > bestScore ||
+ (score == bestScore && value > bestValue))
+ {
+ best = sensor;
+ bestScore = score;
+ bestValue = value;
+ }
+ }
+
+ return best;
+ }
+
+ private static IEnumerable EnumerateSensors(IHardware hardware)
+ {
+ foreach (var sensor in hardware.Sensors)
+ {
+ yield return sensor;
+ }
+
+ foreach (var sub in hardware.SubHardware)
+ {
+ foreach (var sensor in EnumerateSensors(sub))
+ {
+ yield return sensor;
+ }
+ }
+ }
+
+ private ISensor? FindBestGpuTempSensor()
+ {
+ var gpu = _sensorMap.CachedGpu;
+ if (gpu == null) return null;
+
+ ISensor? best = null;
+ int bestScore = int.MinValue;
+ float bestValue = float.MinValue;
+
+ foreach (var sensor in EnumerateSensors(gpu))
+ {
+ int score = SensorMatcher.ScoreGpuTempSensor(sensor);
+ if (score == int.MinValue) continue;
+
+ float value = sensor.Value ?? float.MinValue;
+
+ if (best == null ||
+ score > bestScore ||
+ (score == bestScore && value > bestValue))
+ {
+ best = sensor;
+ bestScore = score;
+ bestValue = value;
+ }
+ }
+
+ return best;
+ }
+
public void ClearCache()
{
lock (_lock)
@@ -337,7 +462,60 @@ public void OnUpdateTickStarted()
}
break;
- // 7. 显存
+ // 7. GPU 负载
+ case "GPU.Load":
+ if (_manualSensorCache.TryGetValue("GPU.Load", out var gpuLoadSensor) &&
+ gpuLoadSensor.Value.HasValue &&
+ !float.IsNaN(gpuLoadSensor.Value.Value))
+ {
+ result = gpuLoadSensor.Value.Value;
+ }
+
+ // 如果当前映射缺失或长期停在 0,自动从当前 GPU 重新挑选更合理的负载传感器
+ if (result == null || result.Value <= 0.01f)
+ {
+ var bestGpuLoad = FindBestGpuLoadSensor();
+ if (bestGpuLoad != null && bestGpuLoad.Value.HasValue && !float.IsNaN(bestGpuLoad.Value.Value))
+ {
+ result = bestGpuLoad.Value.Value;
+ _manualSensorCache["GPU.Load"] = bestGpuLoad;
+ }
+ }
+
+ if (result == null && _lastValidMap.TryGetValue("GPU.Load", out var lastGpuLoad))
+ {
+ result = lastGpuLoad;
+ }
+
+ if (result == null) result = 0f;
+ result = Math.Clamp(result.Value, 0f, 100f);
+ break;
+
+ // 7. GPU 温度 / 显存
+ case "GPU.Temp":
+ if (_manualSensorCache.TryGetValue("GPU.Temp", out var gpuTempSensor) &&
+ gpuTempSensor.Value.HasValue &&
+ !float.IsNaN(gpuTempSensor.Value.Value))
+ {
+ result = gpuTempSensor.Value.Value;
+ }
+
+ if (result == null || result.Value <= 0.01f)
+ {
+ var bestGpuTemp = FindBestGpuTempSensor();
+ if (bestGpuTemp != null && bestGpuTemp.Value.HasValue && !float.IsNaN(bestGpuTemp.Value.Value))
+ {
+ result = bestGpuTemp.Value.Value;
+ _manualSensorCache["GPU.Temp"] = bestGpuTemp;
+ }
+ }
+
+ if (result == null && _lastValidMap.TryGetValue("GPU.Temp", out var lastGpuTemp))
+ {
+ result = lastGpuTemp;
+ }
+ break;
+
case "GPU.VRAM":
float? used = GetValue("GPU.VRAM.Used");
float? total = GetValue("GPU.VRAM.Total");
@@ -479,4 +657,4 @@ public void Dispose()
{
}
}
-}
\ No newline at end of file
+}
diff --git a/src/System/HardwareServices/SensorMap.cs b/src/System/HardwareServices/SensorMap.cs
index 4e1e891..400262e 100644
--- a/src/System/HardwareServices/SensorMap.cs
+++ b/src/System/HardwareServices/SensorMap.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Text;
using LibreHardwareMonitor.Hardware;
using LiteMonitor.src.Core;
@@ -185,6 +187,17 @@ void RegisterTo(IHardware hw)
// 2. 如果优先级相同 (通常是同一张显卡的不同传感器,或者是两张同样的显卡),
// 则应用 D3D vs Vendor 优选逻辑。
+ if (key == "GPU.Temp")
+ {
+ int existingScore = SensorMatcher.ScoreGpuTempSensor(existing);
+ int newScore = SensorMatcher.ScoreGpuTempSensor(s);
+ if (newScore > existingScore)
+ {
+ newMap[key] = s;
+ }
+ continue;
+ }
+
bool existingIsD3D = existing.Name.Contains("D3D", StringComparison.OrdinalIgnoreCase);
bool newIsD3D = s.Name.Contains("D3D", StringComparison.OrdinalIgnoreCase);
@@ -277,9 +290,72 @@ void RegisterTo(IHardware hw)
_lastMapBuild = DateTime.Now;
// ★★★ [优化 3] 指纹记录已移除 ★★★
}
+
+ WriteDiagnostics(computer, newMap, newGpu, newCpuCache);
}
// 复用 HardwareRules 的字符串匹配,避免重复造轮子
public static bool Has(string source, string sub) => HardwareRules.Has(source, sub);
+
+ private static void WriteDiagnostics(
+ Computer computer,
+ Dictionary newMap,
+ IHardware? newGpu,
+ List newCpuCache)
+ {
+ if (!string.Equals(Environment.GetEnvironmentVariable("LITEMONITOR_DIAG"), "1", StringComparison.OrdinalIgnoreCase))
+ return;
+
+ try
+ {
+ var sb = new StringBuilder(8192);
+ sb.AppendLine("==== LiteMonitor Hardware Diagnostics ====");
+ sb.AppendLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
+ sb.AppendLine();
+ sb.AppendLine("[Selected]");
+ sb.AppendLine($"GPU={newGpu?.Name ?? ""}");
+ sb.AppendLine($"CPUCoreCache={newCpuCache.Count}");
+ sb.AppendLine();
+ sb.AppendLine("[Mapped Keys]");
+
+ foreach (var kv in newMap.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
+ {
+ string hwName = kv.Value.Hardware?.Name ?? "";
+ string sensorName = kv.Value.Name ?? "";
+ string sensorType = kv.Value.SensorType.ToString();
+ string value = kv.Value.Value?.ToString("0.###") ?? "null";
+ sb.AppendLine($"{kv.Key} => {sensorType} | {sensorName} [{hwName}] | {value}");
+ }
+
+ sb.AppendLine();
+ sb.AppendLine("[Hardware Tree]");
+ foreach (var hw in computer.Hardware)
+ {
+ DumpHardware(sb, hw, 0);
+ }
+
+ File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "hardware_diagnostic.log"), sb.ToString(), Encoding.UTF8);
+ }
+ catch
+ {
+ }
+ }
+
+ private static void DumpHardware(StringBuilder sb, IHardware hw, int depth)
+ {
+ string indent = new string(' ', depth * 2);
+ sb.AppendLine($"{indent}- {hw.HardwareType}: {hw.Name}");
+
+ foreach (var s in hw.Sensors.OrderBy(x => x.SensorType).ThenBy(x => x.Name, StringComparer.OrdinalIgnoreCase))
+ {
+ string value = s.Value?.ToString("0.###") ?? "null";
+ sb.AppendLine($"{indent} * {s.SensorType}: {s.Name} = {value}");
+ }
+
+ foreach (var sub in hw.SubHardware)
+ {
+ DumpHardware(sb, sub, depth + 1);
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/System/HardwareServices/SensorMatcher.cs b/src/System/HardwareServices/SensorMatcher.cs
index ca0c78c..1532ea5 100644
--- a/src/System/HardwareServices/SensorMatcher.cs
+++ b/src/System/HardwareServices/SensorMatcher.cs
@@ -12,6 +12,69 @@ public static class SensorMatcher
{
// 复用 HardwareRules 的字符串匹配,避免重复造轮子
private static bool Has(string source, string sub) => HardwareRules.Has(source, sub);
+
+ // 统一 GPU 负载命名识别:覆盖不同厂商/驱动的常见命名,并排除干扰项
+ private static bool IsGpuLoadName(string name)
+ {
+ bool include =
+ Has(name, "core") ||
+ Has(name, "d3d 3d") ||
+ Has(name, "3d") ||
+ Has(name, "graphics") ||
+ Has(name, "gpu") ||
+ Has(name, "render") ||
+ Has(name, "utilization") ||
+ Has(name, "global") ||
+ name.Equals("load", StringComparison.OrdinalIgnoreCase);
+
+ if (!include) return false;
+
+ bool exclude =
+ Has(name, "memory") ||
+ Has(name, "video") ||
+ Has(name, "encode") ||
+ Has(name, "decode") ||
+ Has(name, "copy") ||
+ Has(name, "bus") ||
+ Has(name, "pcie") ||
+ Has(name, "media");
+
+ return !exclude;
+ }
+
+ public static int ScoreGpuTempSensor(ISensor sensor)
+ {
+ if (sensor.SensorType != SensorType.Temperature) return int.MinValue;
+
+ string name = sensor.Name ?? "";
+ if (string.IsNullOrWhiteSpace(name)) return int.MinValue;
+
+ bool exclude =
+ Has(name, "memory") ||
+ Has(name, "vram") ||
+ Has(name, "hbm") ||
+ Has(name, "vrm") ||
+ Has(name, "liquid") ||
+ Has(name, "coolant");
+
+ if (exclude) return int.MinValue;
+
+ int score = 0;
+
+ if (Has(name, "core")) score += 120;
+ if (Has(name, "package")) score += 110;
+ if (Has(name, "gpu")) score += 100;
+ if (Has(name, "edge")) score += 95;
+ if (Has(name, "junction")) score += 90;
+ if (Has(name, "hot spot") || Has(name, "hotspot")) score += 85;
+ if (Has(name, "temperature")) score += 40;
+ if (Has(name, "temp")) score += 20;
+ if (Has(name, "soc")) score += 10;
+ if (name.Equals("temperature", StringComparison.OrdinalIgnoreCase)) score += 60;
+ if (name.Equals("gpu", StringComparison.OrdinalIgnoreCase)) score += 10;
+
+ return score > 0 ? score : int.MinValue;
+ }
///
/// 尝试匹配传感器名称到标准 Key
@@ -71,8 +134,8 @@ public static class SensorMatcher
// --- GPU ---
if (type is HardwareType.GpuNvidia or HardwareType.GpuAmd or HardwareType.GpuIntel)
{
- if (s.SensorType == SensorType.Load && (Has(name, "core") || Has(name, "d3d 3d"))) return "GPU.Load";
- if (s.SensorType == SensorType.Temperature && (Has(name, "core") || Has(name, "hot spot") || Has(name, "soc") || Has(name, "vr"))) return "GPU.Temp";
+ if (s.SensorType == SensorType.Load && IsGpuLoadName(name)) return "GPU.Load";
+ if (ScoreGpuTempSensor(s) != int.MinValue) return "GPU.Temp";
// VRAM Logic (简化且准确)
// 1. 根据硬件规则判断是否应该优先找共享内存 (核显)
diff --git a/src/System/Program.cs b/src/System/Program.cs
index 38eb757..0c60612 100644
--- a/src/System/Program.cs
+++ b/src/System/Program.cs
@@ -133,6 +133,8 @@ static void LogCrash(Exception? ex, string source)
$"[Time]: {DateTime.Now}\n" +
$"[Source]: {source}\n" +
$"[Message]: {ex.Message}\n" +
+ $"[Exception]: {ex.GetType().FullName}\n" +
+ $"[Detail]: {ex}\n" +
$"[Stack]:\n{ex.StackTrace}\n" +
"==================================================\n\n";
@@ -148,4 +150,4 @@ static void LogCrash(Exception? ex, string source)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/UI/Helpers/MainFormBizHelper.cs b/src/UI/Helpers/MainFormBizHelper.cs
index 78f9911..20bd738 100644
--- a/src/UI/Helpers/MainFormBizHelper.cs
+++ b/src/UI/Helpers/MainFormBizHelper.cs
@@ -159,7 +159,14 @@ public void RebuildMenus()
{
if (_form.ContextMenuStrip != null)
{
- _form.ContextMenuStrip.Dispose();
+ try
+ {
+ _form.ContextMenuStrip.Dispose();
+ }
+ catch (TypeLoadException)
+ {
+ // single-file + trimmed 下,WinForms ToolStrip 清理链可能抛 TypeLoadException,忽略以保证运行。
+ }
_form.ContextMenuStrip = null;
}
_form.ContextMenuStrip = MenuManager.Build((MainForm)_form, _cfg, _ui);
diff --git a/src/UI/Helpers/TaskbarBizHelper.cs b/src/UI/Helpers/TaskbarBizHelper.cs
index 6b446bf..aae1f9e 100644
--- a/src/UI/Helpers/TaskbarBizHelper.cs
+++ b/src/UI/Helpers/TaskbarBizHelper.cs
@@ -16,6 +16,7 @@ public class TaskbarBizHelper
private readonly Form _form;
private readonly Settings _cfg;
private readonly TaskbarWinHelper _winHelper;
+ private readonly string _targetDevice;
private Rectangle _taskbarRect = Rectangle.Empty;
private int _taskbarHeight = 32;
@@ -33,11 +34,12 @@ public class TaskbarBizHelper
public Color TransparentKey => _transparentKey;
public bool LastIsLightTheme => _lastIsLightTheme;
- public TaskbarBizHelper(Form form, Settings cfg, TaskbarWinHelper winHelper)
+ public TaskbarBizHelper(Form form, Settings cfg, TaskbarWinHelper winHelper, string targetDevice)
{
_form = form;
_cfg = cfg;
_winHelper = winHelper;
+ _targetDevice = targetDevice;
_isWin11 = Environment.OSVersion.Version >= new Version(10, 0, 22000);
}
@@ -84,7 +86,7 @@ public void CheckTheme(bool force = false)
// =================================================================
public void FindHandles()
{
- var handles = _winHelper.FindHandles(_cfg.TaskbarMonitorDevice);
+ var handles = _winHelper.FindHandles(_targetDevice);
_hTaskbar = handles.hTaskbar;
_hTray = handles.hTray;
}
@@ -104,7 +106,7 @@ public void AttachToTaskbar()
public void UpdateTaskbarRect()
{
- _taskbarRect = _winHelper.GetTaskbarRect(_hTaskbar, _cfg.TaskbarMonitorDevice);
+ _taskbarRect = _winHelper.GetTaskbarRect(_hTaskbar, _targetDevice);
_taskbarHeight = Math.Max(24, _taskbarRect.Height);
}
diff --git a/src/UI/Helpers/TaskbarWinHelper.cs b/src/UI/Helpers/TaskbarWinHelper.cs
index 371fcf5..584c9a7 100644
--- a/src/UI/Helpers/TaskbarWinHelper.cs
+++ b/src/UI/Helpers/TaskbarWinHelper.cs
@@ -215,7 +215,7 @@ private IntPtr FindSecondaryTaskbar(Screen screen)
if (screen.Bounds.Contains(r.Location) || screen.Bounds.IntersectsWith(r))
return hWnd;
}
- return FindWindow("Shell_TrayWnd", null);
+ return IntPtr.Zero;
}
public Rectangle GetTaskbarRect(IntPtr hTaskbar, string targetDevice)
diff --git a/src/UI/MainForm_Transparent.cs b/src/UI/MainForm_Transparent.cs
index 1aa385e..567cfea 100644
--- a/src/UI/MainForm_Transparent.cs
+++ b/src/UI/MainForm_Transparent.cs
@@ -3,6 +3,7 @@
using LiteMonitor.src.UI;
using LiteMonitor.src.UI.Helpers;
using System;
+using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
@@ -21,6 +22,7 @@ public class MainForm : Form
private readonly MainFormBizHelper _bizHelper;
private readonly int _wmTaskbarCreated;
private const int WM_DISPLAYCHANGE = 0x007E;
+ private const int WM_GETOBJECT = 0x003D;
private CancellationTokenSource _displayChangeCts;
private Point _dragOffset;
@@ -67,48 +69,90 @@ protected override CreateParams CreateParams
public void CleanMemory() => _bizHelper.CleanMemory();
// ==== 任务栏显示 ====
- private TaskbarForm? _taskbar;
+ private readonly Dictionary _taskbars = new();
public void ToggleTaskbar(bool show)
{
if (show)
{
- if (_taskbar != null && !_taskbar.IsDisposed)
+ if (_ui == null) return;
+
+ var desiredDevices = GetDesiredTaskbarDevices();
+ var staleKeys = _taskbars.Keys.Where(x => !desiredDevices.Contains(x, StringComparer.OrdinalIgnoreCase)).ToList();
+
+ foreach (var key in staleKeys)
{
- if (_taskbar.TargetDevice != _cfg.TaskbarMonitorDevice)
+ if (_taskbars.TryGetValue(key, out var stale))
{
- _taskbar.Close();
- _taskbar.Dispose();
- _taskbar = null;
+ if (!stale.IsDisposed) stale.Close();
+ _taskbars.Remove(key);
}
}
- if (_taskbar == null || _taskbar.IsDisposed)
+ foreach (var device in desiredDevices)
{
- if (_ui != null)
+ if (!_taskbars.TryGetValue(device, out var taskbar) || taskbar.IsDisposed)
{
- _taskbar = new TaskbarForm(_cfg, _ui, this);
- _taskbar.Show();
+ taskbar = new TaskbarForm(_cfg, _ui, this, device);
+ _taskbars[device] = taskbar;
+ taskbar.Show();
+ continue;
}
- }
- else
- {
- if (!_taskbar.Visible)
+
+ if (!taskbar.Visible)
{
- _taskbar.Show();
- _taskbar.ReloadLayout();
+ taskbar.Show();
}
+
+ taskbar.ReloadLayout();
}
}
else
{
- if (_taskbar != null)
+ foreach (var taskbar in _taskbars.Values.ToList())
{
- _taskbar.Close();
- _taskbar.Dispose();
- _taskbar = null;
+ if (!taskbar.IsDisposed) taskbar.Close();
}
+ _taskbars.Clear();
+ }
+ }
+
+ private List GetDesiredTaskbarDevices()
+ {
+ var configuredDevices = (_cfg.TaskbarMonitorDevices ?? new List())
+ .Select(NormalizeTaskbarDevice)
+ .Where(name => !string.IsNullOrWhiteSpace(name))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
+
+ if (configuredDevices.Count > 0)
+ {
+ return configuredDevices;
+ }
+
+ if (_cfg.TaskbarShowOnAllScreens)
+ {
+ return Screen.AllScreens
+ .Select(screen => NormalizeTaskbarDevice(screen.DeviceName))
+ .Where(name => !string.IsNullOrEmpty(name))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
}
+
+ return new List { NormalizeTaskbarDevice(_cfg.TaskbarMonitorDevice) };
+ }
+
+ private static string NormalizeTaskbarDevice(string? deviceName)
+ {
+ if (string.IsNullOrWhiteSpace(deviceName))
+ {
+ return Screen.PrimaryScreen?.DeviceName ?? "";
+ }
+
+ var matched = Screen.AllScreens.FirstOrDefault(s =>
+ string.Equals(s.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
+
+ return matched?.DeviceName ?? deviceName;
}
// ========== 构造函数 ==========
@@ -229,6 +273,13 @@ public void HideMainWindow()
protected override void WndProc(ref Message m)
{
+ if (m.Msg == WM_GETOBJECT)
+ {
+ // 裁剪发布在部分环境下会触发 WinForms 可访问性类型加载异常,直接忽略此消息避免异常刷屏。
+ m.Result = IntPtr.Zero;
+ return;
+ }
+
if (m.Msg == _wmTaskbarCreated && _wmTaskbarCreated != 0)
{
// [Fix] Explorer 重启后,子窗口 TaskbarForm 会被销毁或失效。
@@ -344,6 +395,7 @@ protected override void OnFormClosed(FormClosedEventArgs e)
_cfg.Save();
TrafficLogger.Save();
src.WebServer.LiteWebServer.Instance?.Stop();
+ ToggleTaskbar(false);
base.OnFormClosed(e);
diff --git a/src/UI/MenuManager.cs b/src/UI/MenuManager.cs
index 68175d8..23dd9a6 100644
--- a/src/UI/MenuManager.cs
+++ b/src/UI/MenuManager.cs
@@ -16,6 +16,19 @@ namespace LiteMonitor
{
public static class MenuManager
{
+ private static Image? TryLoadMenuImage(Func resourceGetter)
+ {
+ try
+ {
+ return resourceGetter();
+ }
+ catch
+ {
+ // 单文件+裁剪发布下,WinForms 资源反序列化可能失败。此处降级为空图标,避免启动崩溃。
+ return null;
+ }
+ }
+
///
/// 构建 LiteMonitor 主菜单(右键菜单 + 托盘菜单)
///
@@ -31,7 +44,7 @@ public static ContextMenuStrip Build(MainForm form, Settings cfg, UIController?
// === 清理内存 ===
var cleanMem = new ToolStripMenuItem(LanguageManager.T("Menu.CleanMemory"));
- cleanMem.Image = Properties.Resources.CleanMem;
+ cleanMem.Image = TryLoadMenuImage(() => Properties.Resources.CleanMem);
cleanMem.Click += (_, __) => form.CleanMemory();
menu.Items.Add(cleanMem);
menu.Items.Add(new ToolStripSeparator());
@@ -354,7 +367,7 @@ void SetMode(bool isHorizontal)
var themeRoot = new ToolStripMenuItem(LanguageManager.T("Menu.Theme"));
// 主题编辑器 (独立窗口,保持原样)
var themeEditor = new ToolStripMenuItem(LanguageManager.T("Menu.ThemeEditor"));
- themeEditor.Image = Properties.Resources.ThemeIcon;
+ themeEditor.Image = TryLoadMenuImage(() => Properties.Resources.ThemeIcon);
themeEditor.Click += (_, __) => new ThemeEditor.ThemeEditorForm().Show();
themeRoot.DropDownItems.Add(themeEditor);
themeRoot.DropDownItems.Add(new ToolStripSeparator());
@@ -381,7 +394,7 @@ void SetMode(bool isHorizontal)
// --- [系统硬件详情] ---
var btnHardware = new ToolStripMenuItem(LanguageManager.T("Menu.HardwareInfo"));
- btnHardware.Image = Properties.Resources.HardwareInfo; // 或者找个图标
+ btnHardware.Image = TryLoadMenuImage(() => Properties.Resources.HardwareInfo); // 或者找个图标
btnHardware.Click += (s, e) =>
{
// 这里的模式是:每次点击都 new 一个新的,关闭即销毁。
@@ -397,7 +410,7 @@ void SetMode(bool isHorizontal)
// 网络测速 (独立窗口,保持原样)
var speedWindow = new ToolStripMenuItem(LanguageManager.T("Menu.Speedtest"));
- speedWindow.Image = Properties.Resources.NetworkIcon;
+ speedWindow.Image = TryLoadMenuImage(() => Properties.Resources.NetworkIcon);
speedWindow.Click += (_, __) =>
{
var f = new SpeedTestForm();
@@ -408,7 +421,7 @@ void SetMode(bool isHorizontal)
// 历史流量统计 (独立窗口,保持原样)
var trafficItem = new ToolStripMenuItem(LanguageManager.T("Menu.Traffic"));
- trafficItem.Image = Properties.Resources.TrafficIcon;
+ trafficItem.Image = TryLoadMenuImage(() => Properties.Resources.TrafficIcon);
trafficItem.Click += (_, __) =>
{
var formHistory = new TrafficHistoryForm(cfg);
@@ -416,11 +429,11 @@ void SetMode(bool isHorizontal)
};
menu.Items.Add(trafficItem);
menu.Items.Add(new ToolStripSeparator());
- // =================================================================
+ // =================================================================
// [新增] 设置中心入口
// =================================================================
var itemSettings = new ToolStripMenuItem(LanguageManager.T("Menu.SettingsPanel"));
- itemSettings.Image = Properties.Resources.Settings;
+ itemSettings.Image = TryLoadMenuImage(() => Properties.Resources.Settings);
// 临时写死中文,等面板做完善了再换成 LanguageManager.T("Menu.Settings")
diff --git a/src/UI/Settings/TaskbarPage.cs b/src/UI/Settings/TaskbarPage.cs
index 60010df..35b4033 100644
--- a/src/UI/Settings/TaskbarPage.cs
+++ b/src/UI/Settings/TaskbarPage.cs
@@ -14,6 +14,7 @@ public class TaskbarPage : SettingsPageBase
private Panel _container;
private List _customColorInputs = new List();
private List _customLayoutInputs = new List();
+ private List _monitorChecks = new List();
private Control _styleCombo;
private CheckBox _chkCustomLayout;
@@ -45,6 +46,7 @@ public TaskbarPage()
private void InitializeUI()
{
CreateGeneralGroup();
+ CreateMonitorGroup();
CreateLayoutGroup();
CreateColorGroup();
}
@@ -117,24 +119,6 @@ private void CreateGeneralGroup()
group.AddToggle(this, "Menu.TaskbarSingleLine", () => Config?.TaskbarSingleLine ?? false, v => { if(Config!=null) Config.TaskbarSingleLine = v; });
group.AddToggle(this, "Menu.TaskbarHoverShowAll", () => Config?.TaskbarHoverShowAll ?? false, v => { if (Config != null) Config.TaskbarHoverShowAll = v; });
group.AddToggle(this, "Menu.ClickThrough", () => Config?.TaskbarClickThrough ?? false, v => { if(Config!=null) Config.TaskbarClickThrough = v; });
-
- // Monitor Selection
- var screens = Screen.AllScreens;
- var screenNames = screens.Select((s, i) => $"{i + 1}: {s.DeviceName.Replace(@"\\.\DISPLAY", "Display ")}{(s.Primary ? " [Main]" : "")}").ToList();
- screenNames.Insert(0, LanguageManager.T("Menu.Auto"));
-
- group.AddComboIndex(this, "Menu.TaskbarMonitor", screenNames.ToArray(),
- () => {
- if (string.IsNullOrEmpty(Config?.TaskbarMonitorDevice)) return 0;
- var idx = Array.FindIndex(screens, s => s.DeviceName == Config.TaskbarMonitorDevice);
- return idx >= 0 ? idx + 1 : 0;
- },
- idx => {
- if (Config == null) return;
- if (idx == 0) Config.TaskbarMonitorDevice = "";
- else Config.TaskbarMonitorDevice = screens[idx - 1].DeviceName;
- }
- );
// Double Click Action
string[] actions = {
@@ -164,6 +148,111 @@ private void CreateGeneralGroup()
AddGroupToPage(group);
}
+ private void CreateMonitorGroup()
+ {
+ var group = new LiteSettingsGroup(LanguageManager.T("Menu.TaskbarMonitorSettings"));
+ AddMonitorSelectionItems(group);
+ group.AddHint(LanguageManager.T("Menu.TaskbarMonitorTip"));
+ AddGroupToPage(group);
+ }
+
+ private void AddMonitorSelectionItems(LiteSettingsGroup group)
+ {
+ _monitorChecks.Clear();
+ var chkAuto = new LiteCheck(false, LanguageManager.T("Menu.Enable"));
+ chkAuto.CheckedChanged += (s, e) =>
+ {
+ if (Config == null || !chkAuto.Checked) return;
+
+ Config.TaskbarMonitorDevices.Clear();
+ Config.TaskbarMonitorDevice = "";
+ Config.TaskbarShowOnAllScreens = false;
+
+ foreach (var check in _monitorChecks.Where(x => !ReferenceEquals(x, chkAuto)))
+ {
+ if (check.Checked) check.Checked = false;
+ }
+ };
+ RegisterRefresh(() => chkAuto.Checked = GetSelectedDevices().Count == 0);
+ _monitorChecks.Add(chkAuto);
+ group.AddItem(new LiteSettingsItem(LanguageManager.T("Menu.TaskbarMonitor"), chkAuto));
+
+ var screens = Screen.AllScreens;
+ for (int i = 0; i < screens.Length; i++)
+ {
+ var screen = screens[i];
+ string deviceName = screen.DeviceName;
+ string label = $"{i + 1}: {deviceName.Replace(@"\\.\DISPLAY", "Display ")}";
+ var chk = new LiteCheck(false, LanguageManager.T("Menu.Enable"));
+
+ chk.CheckedChanged += (s, e) =>
+ {
+ if (Config == null) return;
+
+ var selected = GetSelectedDevices();
+ if (chk.Checked)
+ {
+ if (!selected.Contains(deviceName, StringComparer.OrdinalIgnoreCase))
+ {
+ selected.Add(deviceName);
+ }
+
+ Config.TaskbarMonitorDevices = selected
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
+ Config.TaskbarMonitorDevice = Config.TaskbarMonitorDevices.FirstOrDefault() ?? "";
+ Config.TaskbarShowOnAllScreens = false;
+
+ if (chkAuto.Checked) chkAuto.Checked = false;
+ }
+ else
+ {
+ selected.RemoveAll(x => string.Equals(x, deviceName, StringComparison.OrdinalIgnoreCase));
+ Config.TaskbarMonitorDevices = selected;
+ Config.TaskbarMonitorDevice = Config.TaskbarMonitorDevices.FirstOrDefault() ?? "";
+ }
+ };
+
+ RegisterRefresh(() =>
+ {
+ var selected = GetSelectedDevices();
+ chk.Checked = selected.Contains(deviceName, StringComparer.OrdinalIgnoreCase);
+ });
+
+ _monitorChecks.Add(chk);
+ group.AddItem(new LiteSettingsItem(label, chk));
+ }
+ }
+
+ private List GetSelectedDevices()
+ {
+ if (Config == null) return new List();
+
+ if (Config.TaskbarShowOnAllScreens)
+ {
+ return Screen.AllScreens
+ .Select(screen => screen.DeviceName)
+ .Where(name => !string.IsNullOrWhiteSpace(name))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
+ }
+
+ if (Config.TaskbarMonitorDevices != null && Config.TaskbarMonitorDevices.Count > 0)
+ {
+ return Config.TaskbarMonitorDevices
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
+ }
+
+ if (!string.IsNullOrWhiteSpace(Config.TaskbarMonitorDevice))
+ {
+ return new List { Config.TaskbarMonitorDevice };
+ }
+
+ return new List();
+ }
+
private void CreateLayoutGroup()
{
var group = new LiteSettingsGroup(LanguageManager.T("Menu.TaskbarCustomLayout"));
@@ -290,4 +379,4 @@ private void AddGroupToPage(LiteSettingsGroup group)
_container.Controls.SetChildIndex(wrapper, 0);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/UI/TaskbarForm.cs b/src/UI/TaskbarForm.cs
index fa3972e..6f31408 100644
--- a/src/UI/TaskbarForm.cs
+++ b/src/UI/TaskbarForm.cs
@@ -35,20 +35,21 @@ public class TaskbarForm : Form
private const int WM_RBUTTONDOWN = 0x0204;
private const int WM_RBUTTONUP = 0x0205;
private const int WM_LBUTTONDBLCLK = 0x0203;
+ private const int WM_GETOBJECT = 0x003D;
private bool _isWin11;
- public TaskbarForm(Settings cfg, UIController ui, MainForm mainForm)
+ public TaskbarForm(Settings cfg, UIController ui, MainForm mainForm, string targetDevice)
{
_cfg = cfg;
_ui = ui;
_mainForm = mainForm;
- TargetDevice = _cfg.TaskbarMonitorDevice;
+ TargetDevice = targetDevice ?? "";
_isWin11 = Environment.OSVersion.Version >= new Version(10, 0, 22000);
// 初始化组件
_winHelper = new TaskbarWinHelper(this);
- _bizHelper = new TaskbarBizHelper(this, _cfg, _winHelper);
+ _bizHelper = new TaskbarBizHelper(this, _cfg, _winHelper, TargetDevice);
// 窗体属性
FormBorderStyle = FormBorderStyle.None;
@@ -111,6 +112,13 @@ protected override void Dispose(bool disposing)
protected override void WndProc(ref Message m)
{
+ if (m.Msg == WM_GETOBJECT)
+ {
+ // 裁剪发布在部分环境下会触发 WinForms 可访问性类型加载异常,直接忽略此消息避免异常刷屏。
+ m.Result = IntPtr.Zero;
+ return;
+ }
+
// [Fix] 兼容性修复:在 Win11 25H2 + StartAllBack 环境下,
// 右键事件会穿透到原生任务栏。
// 因此不再区分系统版本,统一拦截右键按下和抬起消息。
@@ -141,7 +149,14 @@ private void ShowContextMenu()
{
if (_currentMenu != null)
{
- _currentMenu.Dispose();
+ try
+ {
+ _currentMenu.Dispose();
+ }
+ catch (TypeLoadException)
+ {
+ // single-file + trimmed 下,WinForms ToolStrip 清理链可能抛 TypeLoadException,忽略以保证运行。
+ }
_currentMenu = null;
}
diff --git a/src/UI/UIController.cs b/src/UI/UIController.cs
index edc3258..a181fc1 100644
--- a/src/UI/UIController.cs
+++ b/src/UI/UIController.cs
@@ -198,7 +198,7 @@ private async void Tick()
}
else
{
- it.Value = _mon.Get(it.Key);
+ SetMetricValue(it, _mon.Get(it.Key));
it.TickSmooth(_cfg.AnimationSpeed);
}
}
@@ -219,7 +219,7 @@ void UpdateItem(MetricItem it)
}
else
{
- it.Value = _mon.Get(it.Key);
+ SetMetricValue(it, _mon.Get(it.Key));
it.TickSmooth(_cfg.AnimationSpeed);
}
}
@@ -319,9 +319,7 @@ private void BuildMetrics()
}
else
{
- float? val = _mon.Get(item.Key);
- item.Value = val;
- if (val.HasValue) item.DisplayValue = val.Value;
+ SetMetricValue(item, _mon.Get(item.Key));
}
currentGroupList.Add(item);
@@ -444,9 +442,14 @@ private MetricItem CreateMetric(MonitorItemConfig cfg)
private void InitMetricValue(MetricItem? item)
{
if (item == null) return;
- float? val = _mon.Get(item.Key);
- item.Value = val;
- if (val.HasValue) item.DisplayValue = val.Value;
+ SetMetricValue(item, _mon.Get(item.Key));
+ }
+
+ private static void SetMetricValue(MetricItem item, float? value)
+ {
+ item.Value = value;
+ item.TextValue = value.HasValue ? null : "--";
+ if (value.HasValue) item.DisplayValue = value.Value;
}
private void CheckTemperatureAlert()
@@ -509,4 +512,4 @@ public void Dispose()
_mon.Dispose();
}
}
-}
\ No newline at end of file
+}
diff --git "a/\344\275\277\347\224\250\350\257\264\346\230\216(\345\277\205\350\257\273).txt" "b/\344\275\277\347\224\250\350\257\264\346\230\216(\345\277\205\350\257\273).txt"
new file mode 100644
index 0000000..4368e9f
--- /dev/null
+++ "b/\344\275\277\347\224\250\350\257\264\346\230\216(\345\277\205\350\257\273).txt"
@@ -0,0 +1,5 @@
+LiteMonitor 使用说明
+
+1. 首次启动后,如果 CPU 温度、频率或功耗无法读取,请允许程序安装所需驱动组件。
+2. 主题、语言和插件资源会随主程序一起放在 resources 目录下,请不要单独删除。
+3. 更完整的功能说明、编译方式和更新说明请查看仓库根目录下的 README.md。