diff --git a/tools/ico_extractor/IconExtractor.cs b/tools/ico_extractor/IconExtractor.cs index 21b4624..c4ae0f1 100644 --- a/tools/ico_extractor/IconExtractor.cs +++ b/tools/ico_extractor/IconExtractor.cs @@ -1,104 +1,179 @@ -using System; -using System.Drawing; -using System.Runtime.InteropServices; -using System.IO; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace AppStore.Tools.IconExtractor -{ - public static class IconExtractor - { - /// - /// 获取ICO文件中的实际尺寸列表 - /// - public static List GetIconDimensions(string filePath) - { - var sizes = new List(); - try - { - using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - using (var reader = new BinaryReader(fs)) - { - // 读取ICO文件头 - reader.ReadUInt16(); // 保留字段 - ushort type = reader.ReadUInt16(); // 1=ICO, 2=CUR - ushort count = reader.ReadUInt16(); // 图像数量 - - if (type != 1) return sizes; // 不是ICO文件 - - // 读取每个图像目录项 - for (int i = 0; i < count; i++) - { - byte width = reader.ReadByte(); - byte height = reader.ReadByte(); - reader.ReadBytes(14); // 跳过其他字段 - - // 宽度/高度为0表示256像素 - int size = width == 0 ? 256 : width; - sizes.Add(size); - } - } - } - catch - { - // 忽略所有错误,返回空列表 - } - return sizes; - } - [DllImport("shell32.dll", CharSet = CharSet.Auto)] - private static extern int ExtractIconEx( - string lpszFile, - int nIconIndex, - [AllowNull] IntPtr[] phiconLarge, - [AllowNull] IntPtr[] phiconSmall, - int nIcons); - - [DllImport("user32.dll", CharSet = CharSet.Auto)] - private static extern bool DestroyIcon(IntPtr handle); - - /// - /// 从文件中提取图标 - /// - /// 文件路径 - /// 图标索引 - /// 是否提取大图标 - /// 提取的图标 - public static Icon ExtractIconFromFile(string filePath, int iconIndex = 0) - { - IntPtr[] hIcons = new IntPtr[1]; - int extractedCount = ExtractIconEx(filePath, iconIndex, hIcons, null, 1); - - if (extractedCount <= 0 || hIcons[0] == IntPtr.Zero) - throw new FileNotFoundException("无法从文件中提取图标"); - - // 直接返回原始图标 - Icon icon = (Icon)Icon.FromHandle(hIcons[0]).Clone(); - DestroyIcon(hIcons[0]); - return icon; - } - - /// - /// 将图标保存为文件 - /// - /// 图标对象 - /// 输出路径 - public static void SaveIconToFile(Icon icon, string outputPath) - { - using (FileStream fs = new FileStream(outputPath, FileMode.Create)) - { - icon.Save(fs); - } - } - - /// - /// 获取文件中的图标数量 - /// - /// 文件路径 - /// 图标数量 - public static int GetIconCount(string filePath) - { - return ExtractIconEx(filePath, -1, null, null, 0); - } - } -} +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.IO; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace AppStore.Tools.IconExtractor +{ + public static class IconExtractor + { + /// + /// 获取ICO文件中的实际尺寸列表 + /// + public static List GetIconDimensions(string filePath) + { + var sizes = new List(); + try + { + using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + using (var reader = new BinaryReader(fs)) + { + // 读取ICO文件头 + reader.ReadUInt16(); // 保留字段 + ushort type = reader.ReadUInt16(); // 1=ICO, 2=CUR + ushort count = reader.ReadUInt16(); // 图像数量 + + if (type != 1) return sizes; // 不是ICO文件 + + // 读取每个图像目录项 + for (int i = 0; i < count; i++) + { + byte width = reader.ReadByte(); + byte height = reader.ReadByte(); + reader.ReadBytes(14); // 跳过其他字段 + + // 宽度/高度为0表示256像素 + int size = width == 0 ? 256 : width; + sizes.Add(size); + } + } + } + catch + { + // 忽略所有错误,返回空列表 + } + return sizes; + } + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + private static extern int ExtractIconEx( + string lpszFile, + int nIconIndex, + [AllowNull] IntPtr[] phiconLarge, + [AllowNull] IntPtr[] phiconSmall, + int nIcons); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern bool DestroyIcon(IntPtr handle); + + /// + /// 从文件中提取图标 + /// + /// 文件路径 + /// 图标索引 + /// 是否提取大图标 + /// 提取的图标 + public enum IconSize + { + Large, + Small + } + + public static Icon ExtractIconFromFile(string filePath, int iconIndex = 0, IconSize size = IconSize.Large) + { + if (string.IsNullOrWhiteSpace(filePath)) + throw new ArgumentNullException(nameof(filePath)); + + // 增强文件检查 + try + { + // 规范化路径 + filePath = Path.GetFullPath(filePath); + + // 检查文件属性(比File.Exists更可靠) + var fileInfo = new FileInfo(filePath); + if (!fileInfo.Exists) + { + throw new FileNotFoundException($"文件不存在或无法访问: {filePath}\n" + + $"当前工作目录: {Environment.CurrentDirectory}\n" + + $"绝对路径: {Path.GetFullPath(filePath)}"); + } + + // 尝试打开文件测试是否被锁定 + try + { + using (var stream = fileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + // 文件可访问 + } + } + catch (IOException ioEx) + { + throw new IOException($"文件被锁定或无法访问: {filePath}", ioEx); + } + } + catch (Exception ex) + { + throw new FileNotFoundException($"无法访问文件: {filePath}\n" + + $"错误详情: {ex.Message}", ex); + } + + // 尝试从索引0到3提取图标 + for (int i = 0; i < 4; i++) + { + IntPtr[] hLargeIcons = new IntPtr[1]; + IntPtr[] hSmallIcons = new IntPtr[1]; + + int extractedCount = ExtractIconEx(filePath, i, hLargeIcons, hSmallIcons, 1); + + if (extractedCount > 0) + { + IntPtr hIcon = size == IconSize.Large ? hLargeIcons[0] : hSmallIcons[0]; + if (hIcon == IntPtr.Zero) + { + // 回退到另一种尺寸 + hIcon = size == IconSize.Large ? hSmallIcons[0] : hLargeIcons[0]; + } + + if (hIcon != IntPtr.Zero) + { + try + { + Icon icon = (Icon)Icon.FromHandle(hIcon).Clone(); + return icon; + } + finally + { + if (hLargeIcons[0] != IntPtr.Zero) DestroyIcon(hLargeIcons[0]); + if (hSmallIcons[0] != IntPtr.Zero) DestroyIcon(hSmallIcons[0]); + } + } + } + } + + // 尝试使用ExtractAssociatedIcon作为后备方案 + try + { + return Icon.ExtractAssociatedIcon(filePath); + } + catch (Exception ex) + { + throw new InvalidOperationException($"无法从文件中提取图标(尝试了索引0-3和ExtractAssociatedIcon)", ex); + } + } + + /// + /// 将图标保存为文件 + /// + /// 图标对象 + /// 输出路径 + public static void SaveIconToFile(Icon icon, string outputPath) + { + using (FileStream fs = new FileStream(outputPath, FileMode.Create)) + { + icon.Save(fs); + } + } + + /// + /// 获取文件中的图标数量 + /// + /// 文件路径 + /// 图标数量 + public static int GetIconCount(string filePath) + { + return ExtractIconEx(filePath, -1, null, null, 0); + } + } +}