diff --git a/PersonalToolBox/Models/ToolItem.cs b/PersonalToolBox/Models/ToolItem.cs
index 4a15d3d..2b238b1 100644
--- a/PersonalToolBox/Models/ToolItem.cs
+++ b/PersonalToolBox/Models/ToolItem.cs
@@ -62,4 +62,12 @@ public class ToolItem
/// 是否在软件启动时自动运行
///
public bool AutoRunOnStart { get; set; }
+
+ ///
+ /// 路径是否指向 .exe 文件(仅运行时判断,不存入 JSON)
+ ///
+ [JsonIgnore]
+ public bool IsExePath =>
+ !IsGroup && !string.IsNullOrWhiteSpace(ExecutablePath) &&
+ ExecutablePath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase);
}
diff --git a/PersonalToolBox/Resources/app.ico b/PersonalToolBox/Resources/app.ico
index f3aeb5f..0461a17 100644
Binary files a/PersonalToolBox/Resources/app.ico and b/PersonalToolBox/Resources/app.ico differ
diff --git a/PersonalToolBox/Views/MainWindow.xaml b/PersonalToolBox/Views/MainWindow.xaml
index 5499187..106b872 100644
--- a/PersonalToolBox/Views/MainWindow.xaml
+++ b/PersonalToolBox/Views/MainWindow.xaml
@@ -17,6 +17,7 @@
+
@@ -93,7 +94,26 @@
-
+
+
+
+
+
+
+
@@ -102,9 +122,13 @@
-
+
+
+
+
+
-
+
diff --git a/PersonalToolBox/Views/MainWindow.xaml.cs b/PersonalToolBox/Views/MainWindow.xaml.cs
index ac640d2..79adaa7 100644
--- a/PersonalToolBox/Views/MainWindow.xaml.cs
+++ b/PersonalToolBox/Views/MainWindow.xaml.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
@@ -229,3 +230,53 @@ public class StringToIconCharConverter : IValueConverter
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
+
+///
+/// string (exe路径) → ImageSource: 提取 .exe 文件自身图标,带缓存
+///
+public class ExePathToIconConverter : IValueConverter
+{
+ private static readonly ConcurrentDictionary Cache = new();
+
+ public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is not string path || path.Length == 0)
+ return null;
+
+ if (!path.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
+ return null;
+
+ return Cache.GetOrAdd(path, key =>
+ {
+ try
+ {
+ if (!File.Exists(key))
+ return null;
+
+ using var icon = System.Drawing.Icon.ExtractAssociatedIcon(key);
+ if (icon == null)
+ return null;
+
+ using var bitmap = icon.ToBitmap();
+ using var ms = new MemoryStream();
+ bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
+ ms.Seek(0, SeekOrigin.Begin);
+
+ var image = new BitmapImage();
+ image.BeginInit();
+ image.CacheOption = BitmapCacheOption.OnLoad;
+ image.StreamSource = ms;
+ image.EndInit();
+ image.Freeze();
+ return image;
+ }
+ catch
+ {
+ return null;
+ }
+ });
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ => throw new NotImplementedException();
+}