From 6fb7af3d46953d02d34dcd7f6623d727fa82aff0 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 26 Jul 2025 14:29:24 +0800 Subject: [PATCH] =?UTF-8?q?add:=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C?= =?UTF-8?q?=E5=88=86=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/App.xaml.cs | 17 + Ink Canvas/Helpers/AutoUpdateHelper.cs | 55 +- Ink Canvas/Helpers/DeviceIdentifier.cs | 2655 +++++++++++++++++ Ink Canvas/MainWindow.xaml | 32 + Ink Canvas/MainWindow.xaml.cs | 95 + .../InkCanvasForClass_MarkupCompile.cache | 2 +- 6 files changed, 2847 insertions(+), 9 deletions(-) create mode 100644 Ink Canvas/Helpers/DeviceIdentifier.cs diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs index c3c3c192..e632c49d 100644 --- a/Ink Canvas/App.xaml.cs +++ b/Ink Canvas/App.xaml.cs @@ -432,6 +432,12 @@ namespace Ink_Canvas LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version.ToString())); + // 记录应用启动(设备标识符) + DeviceIdentifier.RecordAppLaunch(); + LogHelper.WriteLogToFile($"App | 设备ID: {DeviceIdentifier.GetDeviceId()}"); + LogHelper.WriteLogToFile($"App | 使用频率: {DeviceIdentifier.GetUsageFrequency()}"); + LogHelper.WriteLogToFile($"App | 更新优先级: {DeviceIdentifier.GetUpdatePriority()}"); + bool ret; mutex = new System.Threading.Mutex(true, "InkCanvasForClass", out ret); @@ -710,6 +716,17 @@ namespace Ink_Canvas string exitType = IsAppExitByUser ? "用户主动退出" : "应用程序退出"; WriteCrashLog($"{exitType},退出代码: {e.ApplicationExitCode}"); + // 记录应用退出(设备标识符) + try + { + DeviceIdentifier.RecordAppExit(); + LogHelper.WriteLogToFile($"App | 应用运行时长: {(DateTime.Now - appStartTime).TotalMinutes:F1}分钟"); + } + catch (Exception deviceEx) + { + LogHelper.WriteLogToFile($"记录设备标识符退出信息失败: {deviceEx.Message}", LogHelper.LogType.Error); + } + if (IsAppExitByUser) { // 写入退出信号文件,通知看门狗正常退出 diff --git a/Ink Canvas/Helpers/AutoUpdateHelper.cs b/Ink Canvas/Helpers/AutoUpdateHelper.cs index 30e54eae..578651c9 100644 --- a/Ink Canvas/Helpers/AutoUpdateHelper.cs +++ b/Ink Canvas/Helpers/AutoUpdateHelper.cs @@ -396,7 +396,7 @@ namespace Ink_Canvas.Helpers } // 通过GitHub API获取最新Release信息 - private static async Task<(string version, string downloadUrl, string releaseNotes)> GetLatestGithubRelease(UpdateChannel channel) + private static async Task<(string version, string downloadUrl, string releaseNotes, DateTime? releaseTime)> GetLatestGithubRelease(UpdateChannel channel) { try { @@ -411,15 +411,23 @@ namespace Ink_Canvas.Helpers string version = json["tag_name"]?.ToString(); string releaseNotes = json["body"]?.ToString(); string downloadUrl = json["assets"]?.First?["browser_download_url"]?.ToString(); + + // 解析发布时间 + DateTime? releaseTime = null; + if (json["published_at"] != null && DateTime.TryParse(json["published_at"].ToString(), out DateTime parsedTime)) + { + releaseTime = parsedTime; + } + if (!string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(downloadUrl)) - return (version, downloadUrl, releaseNotes); + return (version, downloadUrl, releaseNotes, releaseTime); } } catch (Exception ex) { LogHelper.WriteLogToFile($"AutoUpdate | GitHub Releases API 获取失败: {ex.Message}", LogHelper.LogType.Warning); } - return (null, null, null); + return (null, null, null, null); } // 主要的更新检测方法(优先检测延迟,失败时自动切换线路组) @@ -428,11 +436,17 @@ namespace Ink_Canvas.Helpers { try { + // 记录更新检查时间 + DeviceIdentifier.RecordUpdateCheck(); + string localVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); LogHelper.WriteLogToFile($"AutoUpdate | 本地版本: {localVersion}"); + LogHelper.WriteLogToFile($"AutoUpdate | 设备ID: {DeviceIdentifier.GetDeviceId()}"); + LogHelper.WriteLogToFile($"AutoUpdate | 更新优先级: {DeviceIdentifier.GetUpdatePriority()}"); LogHelper.WriteLogToFile($"AutoUpdate | 优先通过GitHub Releases API检测..."); + // 1. 优先通过GitHub Releases API获取 - var (apiVersion, _, apiReleaseNotes) = await GetLatestGithubRelease(channel); + var (apiVersion, _, apiReleaseNotes, apiReleaseTime) = await GetLatestGithubRelease(channel); if (!string.IsNullOrEmpty(apiVersion)) { Version local = new Version(localVersion); @@ -440,15 +454,29 @@ namespace Ink_Canvas.Helpers if (remote > local || alwaysGetRemote) { LogHelper.WriteLogToFile($"AutoUpdate | 通过GitHub Releases API发现新版本: {apiVersion}"); + + // 检查是否应该根据用户优先级推送更新 + DateTime releaseTime = apiReleaseTime ?? DateTime.Now; + bool shouldPush = DeviceIdentifier.ShouldPushUpdate(apiVersion, releaseTime); + if (!shouldPush) + { + var priority = DeviceIdentifier.GetUpdatePriority(); + var daysSinceRelease = (DateTime.Now - releaseTime).TotalDays; + LogHelper.WriteLogToFile($"AutoUpdate | 根据用户优先级({priority}),暂不推送更新 {apiVersion},发布时间: {releaseTime:yyyy-MM-dd HH:mm:ss},已过 {daysSinceRelease:F1} 天"); + var group = (await GetAvailableLineGroupsOrdered(channel)).FirstOrDefault(); + return (null, group, apiReleaseNotes); // 返回null表示不推送 + } + + LogHelper.WriteLogToFile($"AutoUpdate | 根据用户优先级,推送更新 {apiVersion}"); // 只返回版本号和日志,不返回直链 - var group = (await GetAvailableLineGroupsOrdered(channel)).FirstOrDefault(); - return (apiVersion, group, apiReleaseNotes); + var availableGroup = (await GetAvailableLineGroupsOrdered(channel)).FirstOrDefault(); + return (apiVersion, availableGroup, apiReleaseNotes); } else { LogHelper.WriteLogToFile($"AutoUpdate | 当前版本已是最新 (GitHub Releases API)"); - var group = (await GetAvailableLineGroupsOrdered(channel)).FirstOrDefault(); - return (null, group, apiReleaseNotes); + var availableGroup = (await GetAvailableLineGroupsOrdered(channel)).FirstOrDefault(); + return (null, availableGroup, apiReleaseNotes); } } // 2. 回退到原有txt方案 @@ -471,6 +499,17 @@ namespace Ink_Canvas.Helpers if (remote > local || alwaysGetRemote) { LogHelper.WriteLogToFile($"AutoUpdate | 发现新版本或强制获取: {remoteVersion}"); + + // 检查是否应该根据用户优先级推送更新 + bool shouldPush = DeviceIdentifier.ShouldPushUpdate(remoteVersion, DateTime.Now); + if (!shouldPush) + { + var priority = DeviceIdentifier.GetUpdatePriority(); + LogHelper.WriteLogToFile($"AutoUpdate | 根据用户优先级({priority}),暂不推送更新 {remoteVersion}"); + return (null, group, null); // 返回null表示不推送 + } + + LogHelper.WriteLogToFile($"AutoUpdate | 根据用户优先级,推送更新 {remoteVersion}"); return (remoteVersion, group, null); } else diff --git a/Ink Canvas/Helpers/DeviceIdentifier.cs b/Ink Canvas/Helpers/DeviceIdentifier.cs new file mode 100644 index 00000000..f9ad2d69 --- /dev/null +++ b/Ink Canvas/Helpers/DeviceIdentifier.cs @@ -0,0 +1,2655 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Microsoft.Win32; + +namespace Ink_Canvas.Helpers +{ + /// + /// 设备标识符和使用频率监控类 + /// + internal static class DeviceIdentifier + { + // 多重备份路径策略 + private static readonly string DeviceIdFilePath = Path.Combine(App.RootPath, "device_id.dat"); + private static readonly string UsageStatsFilePath = Path.Combine(App.RootPath, "usage_stats.json"); + + // 使用频率数据的多重隐藏备份路径 + private static readonly string BackupDeviceIdPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "ICC", ".sys", "device.dat"); + private static readonly string BackupUsageStatsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "ICC", ".sys", "usage.dat"); + + // 使用频率数据的额外隐藏备份位置 + private static readonly string SecondaryUsageBackupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Microsoft", "Windows", ".icc", "usage_backup.tmp"); + private static readonly string TertiaryUsageBackupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), + "ICC", ".cache", "usage_cache.dat"); + private static readonly string QuaternaryUsageBackupPath = Path.Combine(Path.GetTempPath(), + ".icc_temp", "usage_temp.dat"); + + // 数据完整性验证密钥 + private static readonly string DataIntegrityKey = "ICC_DEVICE_INTEGRITY_2024"; + + private static readonly string DeviceId = null; + private static readonly object fileLock = new object(); + + static DeviceIdentifier() + { + // 在静态构造函数中初始化设备ID + DeviceId = GetOrCreateDeviceId(); + + // 执行数据完整性检查和自动修复 + try + { + PerformDataIntegrityCheck(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 初始化时数据完整性检查失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 获取或创建设备ID + /// + /// 25字符的唯一设备标识符 + public static string GetDeviceId() + { + return DeviceId; + } + + /// + /// 获取或创建设备ID(内部方法)- 支持多重备份恢复 + /// + private static string GetOrCreateDeviceId() + { + lock (fileLock) + { + try + { + // 1. 尝试从主文件读取设备ID + string deviceId = LoadDeviceIdFromFile(DeviceIdFilePath); + if (!string.IsNullOrEmpty(deviceId)) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从主文件读取设备ID: {deviceId}"); + // 确保备份同步 + SaveDeviceIdToAllLocations(deviceId); + return deviceId; + } + + // 2. 尝试从备份文件恢复 + deviceId = LoadDeviceIdFromFile(BackupDeviceIdPath); + if (!string.IsNullOrEmpty(deviceId)) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从备份文件恢复设备ID: {deviceId}"); + SaveDeviceIdToAllLocations(deviceId); + return deviceId; + } + + // 3. 尝试从注册表恢复 + deviceId = LoadDeviceIdFromRegistry(); + if (!string.IsNullOrEmpty(deviceId)) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表恢复设备ID: {deviceId}"); + SaveDeviceIdToAllLocations(deviceId); + return deviceId; + } + + // 4. 生成新的设备ID + string newDeviceId = GenerateDeviceId(); + LogHelper.WriteLogToFile($"DeviceIdentifier | 生成新设备ID: {newDeviceId}"); + + // 5. 保存到所有位置 + SaveDeviceIdToAllLocations(newDeviceId); + + return newDeviceId; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 获取设备ID时出错: {ex.Message}", LogHelper.LogType.Error); + // 返回一个基于时间戳的备用ID + return GenerateFallbackDeviceId(); + } + } + } + + /// + /// 生成25字符的唯一设备ID + /// + private static string GenerateDeviceId() + { + try + { + // 收集硬件信息 + var hardwareInfo = new StringBuilder(); + + // 使用反射获取硬件信息,避免直接引用System.Management + try + { + // 尝试加载System.Management程序集 + var assembly = System.Reflection.Assembly.Load("System.Management"); + if (assembly != null) + { + // CPU信息 + try + { + var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher"); + var searcher = Activator.CreateInstance(searcherType, "SELECT ProcessorId FROM Win32_Processor"); + var getMethod = searcherType.GetMethod("Get"); + var enumerator = getMethod.Invoke(searcher, null); + + var moveNextMethod = enumerator.GetType().GetMethod("MoveNext"); + var currentProperty = enumerator.GetType().GetProperty("Current"); + + if ((bool)moveNextMethod.Invoke(enumerator, null)) + { + var obj = currentProperty.GetValue(enumerator); + var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) }); + var processorId = indexer.GetValue(obj, new object[] { "ProcessorId" }); + hardwareInfo.Append(processorId?.ToString() ?? ""); + } + + var disposeMethod = searcher.GetType().GetMethod("Dispose"); + disposeMethod?.Invoke(searcher, null); + } + catch { } + + // 主板序列号 + try + { + var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher"); + var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_BaseBoard"); + var getMethod = searcherType.GetMethod("Get"); + var enumerator = getMethod.Invoke(searcher, null); + + var moveNextMethod = enumerator.GetType().GetMethod("MoveNext"); + var currentProperty = enumerator.GetType().GetProperty("Current"); + + if ((bool)moveNextMethod.Invoke(enumerator, null)) + { + var obj = currentProperty.GetValue(enumerator); + var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) }); + var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" }); + hardwareInfo.Append(serialNumber?.ToString() ?? ""); + } + + var disposeMethod = searcher.GetType().GetMethod("Dispose"); + disposeMethod?.Invoke(searcher, null); + } + catch { } + + // BIOS序列号 + try + { + var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher"); + var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_BIOS"); + var getMethod = searcherType.GetMethod("Get"); + var enumerator = getMethod.Invoke(searcher, null); + + var moveNextMethod = enumerator.GetType().GetMethod("MoveNext"); + var currentProperty = enumerator.GetType().GetProperty("Current"); + + if ((bool)moveNextMethod.Invoke(enumerator, null)) + { + var obj = currentProperty.GetValue(enumerator); + var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) }); + var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" }); + hardwareInfo.Append(serialNumber?.ToString() ?? ""); + } + + var disposeMethod = searcher.GetType().GetMethod("Dispose"); + disposeMethod?.Invoke(searcher, null); + } + catch { } + + // 主硬盘序列号 + try + { + var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher"); + var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_DiskDrive WHERE MediaType='Fixed hard disk media'"); + var getMethod = searcherType.GetMethod("Get"); + var enumerator = getMethod.Invoke(searcher, null); + + var moveNextMethod = enumerator.GetType().GetMethod("MoveNext"); + var currentProperty = enumerator.GetType().GetProperty("Current"); + + if ((bool)moveNextMethod.Invoke(enumerator, null)) + { + var obj = currentProperty.GetValue(enumerator); + var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) }); + var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" }); + hardwareInfo.Append(serialNumber?.ToString() ?? ""); + } + + var disposeMethod = searcher.GetType().GetMethod("Dispose"); + disposeMethod?.Invoke(searcher, null); + } + catch { } + } + } + catch { } + + // 如果硬件信息不足,添加系统信息 + if (hardwareInfo.Length < 10) + { + hardwareInfo.Append(Environment.MachineName); + hardwareInfo.Append(Environment.UserName); + hardwareInfo.Append(Environment.OSVersion.ToString()); + } + + // 生成哈希 + using (var sha256 = SHA256.Create()) + { + byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(hardwareInfo.ToString())); + string hashString = BitConverter.ToString(hashBytes).Replace("-", ""); + + // 取前25个字符,确保唯一性 + string deviceId = hashString.Substring(0, 25); + + // 添加校验位(第25位) + int checksum = 0; + for (int i = 0; i < 24; i++) + { + checksum += Convert.ToInt32(deviceId[i]); + } + checksum %= 36; // 0-9, A-Z + char checksumChar = checksum < 10 ? (char)(checksum + '0') : (char)(checksum - 10 + 'A'); + + return deviceId.Substring(0, 24) + checksumChar; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 生成设备ID时出错: {ex.Message}", LogHelper.LogType.Error); + return GenerateFallbackDeviceId(); + } + } + + /// + /// 生成备用设备ID(基于时间戳) + /// + private static string GenerateFallbackDeviceId() + { + try + { + string timestamp = DateTime.Now.Ticks.ToString("X"); + string random = Guid.NewGuid().ToString("N").Substring(0, 8); + string combined = timestamp + random; + + using (var sha256 = SHA256.Create()) + { + byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(combined)); + string hashString = BitConverter.ToString(hashBytes).Replace("-", ""); + return hashString.Substring(0, 25); + } + } + catch + { + // 最后的备用方案 + return "ICC" + DateTime.Now.ToString("yyyyMMddHHmmss") + "000000000"; + } + } + + /// + /// 验证设备ID格式 + /// + private static bool IsValidDeviceId(string deviceId) + { + if (string.IsNullOrEmpty(deviceId) || deviceId.Length != 25) + return false; + + // 验证字符集(只允许数字和大写字母) + if (!deviceId.All(c => char.IsLetterOrDigit(c) && (char.IsDigit(c) || char.IsUpper(c)))) + return false; + + // 验证校验位 + try + { + int checksum = 0; + for (int i = 0; i < 24; i++) + { + checksum += Convert.ToInt32(deviceId[i]); + } + checksum %= 36; + char expectedChecksum = checksum < 10 ? (char)(checksum + '0') : (char)(checksum - 10 + 'A'); + return deviceId[24] == expectedChecksum; + } + catch + { + return false; + } + } + + /// + /// 从文件加载设备ID + /// + private static string LoadDeviceIdFromFile(string filePath) + { + try + { + if (File.Exists(filePath)) + { + string content = File.ReadAllText(filePath).Trim(); + if (IsValidDeviceId(content)) + { + return content; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从文件加载设备ID失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error); + } + return null; + } + + /// + /// 从注册表加载设备ID + /// + private static string LoadDeviceIdFromRegistry() + { + try + { + using (var key = Registry.CurrentUser.OpenSubKey(@"Software\ICC\DeviceInfo")) + { + if (key != null) + { + var value = key.GetValue("DeviceId") as string; + if (IsValidDeviceId(value)) + { + return value; + } + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表加载设备ID失败: {ex.Message}", LogHelper.LogType.Error); + } + return null; + } + + /// + /// 保存设备ID到所有位置 + /// + private static void SaveDeviceIdToAllLocations(string deviceId) + { + // 保存到主文件 + SaveDeviceIdToFile(DeviceIdFilePath, deviceId); + + // 保存到备份文件 + SaveDeviceIdToFile(BackupDeviceIdPath, deviceId); + + // 保存到注册表 + SaveDeviceIdToRegistry(deviceId); + } + + /// + /// 保存设备ID到文件 + /// + private static void SaveDeviceIdToFile(string filePath, string deviceId) + { + try + { + // 确保目录存在 + var directory = Path.GetDirectoryName(filePath); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + // 设置隐藏属性 + if (filePath.Contains(".sys")) + { + var dirInfo = new DirectoryInfo(directory); + dirInfo.Attributes |= FileAttributes.Hidden | FileAttributes.System; + } + } + + File.WriteAllText(filePath, deviceId); + + // 设置文件属性为隐藏和系统文件 + if (filePath.Contains(".sys")) + { + var fileInfo = new FileInfo(filePath); + fileInfo.Attributes |= FileAttributes.Hidden | FileAttributes.System; + } + + LogHelper.WriteLogToFile($"DeviceIdentifier | 设备ID已保存到: {filePath}"); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 保存设备ID到文件失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 保存设备ID到注册表 + /// + private static void SaveDeviceIdToRegistry(string deviceId) + { + try + { + using (var key = Registry.CurrentUser.CreateSubKey(@"Software\ICC\DeviceInfo")) + { + key?.SetValue("DeviceId", deviceId); + key?.SetValue("LastUpdate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + } + LogHelper.WriteLogToFile($"DeviceIdentifier | 设备ID已保存到注册表"); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 保存设备ID到注册表失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 使用频率统计数据结构 + /// + private class UsageStats + { + [JsonProperty("deviceId")] + public string DeviceId { get; set; } + + [JsonProperty("lastLaunchTime")] + public DateTime LastLaunchTime { get; set; } + + [JsonProperty("launchCount")] + public int LaunchCount { get; set; } + + [JsonProperty("totalUsageMinutes")] + public long TotalUsageMinutes { get; set; } + + [JsonProperty("averageSessionMinutes")] + public double AverageSessionMinutes { get; set; } + + [JsonProperty("lastUpdateCheck")] + public DateTime LastUpdateCheck { get; set; } + + [JsonProperty("updatePriority")] + public UpdatePriority UpdatePriority { get; set; } + + [JsonProperty("usageFrequency")] + public UsageFrequency UsageFrequency { get; set; } + + [JsonProperty("dataHash")] + public string DataHash { get; set; } + + [JsonProperty("lastModified")] + public DateTime LastModified { get; set; } + + /// + /// 计算数据哈希值用于完整性验证 + /// + public void UpdateDataHash() + { + var dataString = $"{DeviceId}|{LaunchCount}|{TotalUsageMinutes}|{LastLaunchTime:yyyyMMddHHmmss}|{DataIntegrityKey}"; + using (var sha256 = SHA256.Create()) + { + var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(dataString)); + DataHash = Convert.ToBase64String(hashBytes); + } + LastModified = DateTime.Now; + } + + /// + /// 验证数据完整性 + /// + public bool VerifyDataIntegrity() + { + try + { + var dataString = $"{DeviceId}|{LaunchCount}|{TotalUsageMinutes}|{LastLaunchTime:yyyyMMddHHmmss}|{DataIntegrityKey}"; + using (var sha256 = SHA256.Create()) + { + var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(dataString)); + var expectedHash = Convert.ToBase64String(hashBytes); + return DataHash == expectedHash; + } + } + catch + { + return false; + } + } + } + + /// + /// 更新优先级枚举 + /// + public enum UpdatePriority + { + High = 1, // 高频用户,优先推送 + Medium = 2, // 中频用户,正常推送 + Low = 3 // 低频用户,延迟推送 + } + + /// + /// 使用频率枚举 + /// + public enum UsageFrequency + { + High = 1, // 高频用户:每周使用超过5次或总时长超过10小时 + Medium = 2, // 中频用户:每周使用2-5次或总时长2-10小时 + Low = 3 // 低频用户:每周使用少于2次或总时长少于2小时 + } + + /// + /// 记录应用启动 + /// + public static void RecordAppLaunch() + { + try + { + lock (fileLock) + { + var stats = LoadUsageStats(); + stats.LastLaunchTime = DateTime.Now; + stats.LaunchCount++; + stats.DeviceId = DeviceId; + + // 计算使用频率 + CalculateUsageFrequency(stats); + + // 更新数据完整性哈希 + stats.UpdateDataHash(); + + SaveUsageStats(stats); + + LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用启动 - 设备ID: {DeviceId}, 启动次数: {stats.LaunchCount}"); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用启动失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 记录应用退出(计算使用时长) + /// + public static void RecordAppExit() + { + try + { + lock (fileLock) + { + var stats = LoadUsageStats(); + + // 计算本次会话时长 + if (stats.LastLaunchTime != DateTime.MinValue) + { + var sessionDuration = DateTime.Now - stats.LastLaunchTime; + stats.TotalUsageMinutes += (long)sessionDuration.TotalMinutes; + + // 更新平均会话时长 + if (stats.LaunchCount > 0) + { + stats.AverageSessionMinutes = (double)stats.TotalUsageMinutes / stats.LaunchCount; + } + } + + // 重新计算使用频率 + CalculateUsageFrequency(stats); + + // 更新数据完整性哈希 + stats.UpdateDataHash(); + + SaveUsageStats(stats); + + LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用退出 - 总使用时长: {stats.TotalUsageMinutes}分钟, 平均会话: {stats.AverageSessionMinutes:F1}分钟"); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用退出失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 计算使用频率和更新优先级(综合考虑时间和使用模式) + /// + private static void CalculateUsageFrequency(UsageStats stats) + { + try + { + // 计算最近活跃度 + var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays; + + // 计算平均使用强度 + var avgMinutesPerLaunch = stats.LaunchCount > 0 ? (double)stats.TotalUsageMinutes / stats.LaunchCount : 0; + + // 估算最近使用情况(基于历史模式) + var estimatedRecentLaunches = EstimateRecentActivity(stats, daysSinceLastUse); + var estimatedRecentMinutes = estimatedRecentLaunches * avgMinutesPerLaunch; + + // 综合评分系统 + var frequencyScore = CalculateFrequencyScore(stats, daysSinceLastUse, estimatedRecentLaunches, estimatedRecentMinutes); + + // 根据综合评分确定频率和优先级 + if (frequencyScore >= 80) + { + stats.UsageFrequency = UsageFrequency.High; + stats.UpdatePriority = UpdatePriority.High; + } + else if (frequencyScore >= 40) + { + stats.UsageFrequency = UsageFrequency.Medium; + stats.UpdatePriority = UpdatePriority.Medium; + } + else + { + stats.UsageFrequency = UsageFrequency.Low; + stats.UpdatePriority = UpdatePriority.Low; + } + + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率计算 - 评分: {frequencyScore}, 频率: {stats.UsageFrequency}, " + + $"优先级: {stats.UpdatePriority}, 最后使用: {daysSinceLastUse:F1}天前"); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 计算使用频率失败: {ex.Message}", LogHelper.LogType.Error); + // 默认设置为中等优先级 + stats.UsageFrequency = UsageFrequency.Medium; + stats.UpdatePriority = UpdatePriority.Medium; + } + } + + /// + /// 估算最近活跃度 + /// + private static int EstimateRecentActivity(UsageStats stats, double daysSinceLastUse) + { + if (daysSinceLastUse > 30) return 0; // 超过30天未使用 + if (daysSinceLastUse > 14) return Math.Min(1, stats.LaunchCount / 50); // 14-30天,很少活动 + if (daysSinceLastUse > 7) return Math.Min(3, stats.LaunchCount / 20); // 7-14天,少量活动 + if (daysSinceLastUse > 3) return Math.Min(7, stats.LaunchCount / 10); // 3-7天,中等活动 + return Math.Min(15, stats.LaunchCount / 5); // 3天内,高活动 + } + + /// + /// 计算综合频率评分(0-100分) + /// + private static int CalculateFrequencyScore(UsageStats stats, double daysSinceLastUse, + int estimatedRecentLaunches, double estimatedRecentMinutes) + { + var score = 0; + + // 最近活跃度评分(40分) + if (daysSinceLastUse <= 1) score += 40; + else if (daysSinceLastUse <= 3) score += 35; + else if (daysSinceLastUse <= 7) score += 25; + else if (daysSinceLastUse <= 14) score += 15; + else if (daysSinceLastUse <= 30) score += 5; + + // 使用频率评分(30分) + if (estimatedRecentLaunches >= 10) score += 30; + else if (estimatedRecentLaunches >= 5) score += 20; + else if (estimatedRecentLaunches >= 2) score += 10; + else if (estimatedRecentLaunches >= 1) score += 5; + + // 使用时长评分(20分) + if (estimatedRecentMinutes >= 600) score += 20; // 10小时以上 + else if (estimatedRecentMinutes >= 300) score += 15; // 5小时以上 + else if (estimatedRecentMinutes >= 120) score += 10; // 2小时以上 + else if (estimatedRecentMinutes >= 60) score += 5; // 1小时以上 + + // 历史使用深度评分(10分) + if (stats.TotalUsageMinutes >= 3000) score += 10; // 重度用户 + else if (stats.TotalUsageMinutes >= 1200) score += 7; // 中度用户 + else if (stats.TotalUsageMinutes >= 300) score += 4; // 轻度用户 + + return Math.Min(100, score); + } + + /// + /// 获取当前更新优先级 + /// + public static UpdatePriority GetUpdatePriority() + { + try + { + var stats = LoadUsageStats(); + return stats.UpdatePriority; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 获取更新优先级失败: {ex.Message}", LogHelper.LogType.Error); + return UpdatePriority.Medium; // 默认中等优先级 + } + } + + /// + /// 获取使用频率 + /// + public static UsageFrequency GetUsageFrequency() + { + try + { + var stats = LoadUsageStats(); + return stats.UsageFrequency; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 获取使用频率失败: {ex.Message}", LogHelper.LogType.Error); + return UsageFrequency.Medium; // 默认中等频率 + } + } + + /// + /// 获取使用统计信息 + /// + public static (int launchCount, long totalMinutes, double avgSession, UpdatePriority priority) GetUsageStats() + { + try + { + var stats = LoadUsageStats(); + return (stats.LaunchCount, stats.TotalUsageMinutes, stats.AverageSessionMinutes, stats.UpdatePriority); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 获取使用统计失败: {ex.Message}", LogHelper.LogType.Error); + return (0, 0, 0, UpdatePriority.Medium); + } + } + + /// + /// 加载使用统计 - 支持多重备份恢复和智能反篡改(强化版本) + /// + private static UsageStats LoadUsageStats() + { + try + { + // 智能恢复:收集所有可用的数据源,选择最可信的 + var allDataSources = CollectAllUsageDataSources(); + + // 如果找到有效数据,返回最可信的 + if (allDataSources.Count > 0) + { + var bestData = SelectMostTrustedData(allDataSources); + if (bestData != null) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用最可信数据源恢复使用统计: {bestData.Source}"); + + // 确保备份同步 + SaveUsageStatsToAllLocations(bestData.Stats); + return bestData.Stats; + } + } + + LogHelper.WriteLogToFile($"DeviceIdentifier | 所有数据源都不可用,检查是否有部分可恢复数据", LogHelper.LogType.Warning); + + // 如果没有完全可信的数据,尝试从部分损坏的数据中恢复 + var partiallyRecoveredData = AttemptPartialDataRecovery(allDataSources); + if (partiallyRecoveredData != null) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从部分损坏数据中恢复使用统计"); + SaveUsageStatsToAllLocations(partiallyRecoveredData); + return partiallyRecoveredData; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 加载使用统计失败: {ex.Message}", LogHelper.LogType.Error); + } + + // 返回新的统计对象 + var newStats = new UsageStats + { + DeviceId = DeviceId, + LastLaunchTime = DateTime.Now, + LaunchCount = 0, + TotalUsageMinutes = 0, + AverageSessionMinutes = 0, + LastUpdateCheck = DateTime.MinValue, + UpdatePriority = UpdatePriority.Medium, + UsageFrequency = UsageFrequency.Medium + }; + + // 更新数据完整性哈希 + newStats.UpdateDataHash(); + + // 保存新统计到所有位置 + SaveUsageStatsToAllLocations(newStats); + return newStats; + } + + /// + /// 保存使用统计 - 多重备份 + /// + private static void SaveUsageStats(UsageStats stats) + { + SaveUsageStatsToAllLocations(stats); + } + + /// + /// 数据源信息结构 + /// + private class DataSourceInfo + { + public UsageStats Stats { get; set; } + public string Source { get; set; } + public bool IsIntegrityValid { get; set; } + public DateTime LastModified { get; set; } + public int TrustScore { get; set; } + } + + /// + /// 从文件加载使用统计(带完整性验证,但不丢弃篡改数据) + /// + private static UsageStats LoadUsageStatsFromFile(string filePath) + { + try + { + if (File.Exists(filePath)) + { + string json = File.ReadAllText(filePath); + var stats = JsonConvert.DeserializeObject(json); + if (stats != null && !string.IsNullOrEmpty(stats.DeviceId)) + { + // 验证数据完整性 + if (!string.IsNullOrEmpty(stats.DataHash)) + { + if (stats.VerifyDataIntegrity()) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性验证通过: {filePath}"); + return stats; + } + else + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性验证失败,可能被篡改: {filePath}", LogHelper.LogType.Warning); + return null; // 数据被篡改,不使用 + } + } + else + { + // 旧版本数据,没有哈希值,更新哈希后返回 + LogHelper.WriteLogToFile($"DeviceIdentifier | 检测到旧版本数据,正在更新完整性哈希: {filePath}"); + stats.UpdateDataHash(); + return stats; + } + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从文件加载使用统计失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error); + } + return null; + } + + /// + /// 从文件加载使用统计(包括被篡改的数据,用于恢复分析) + /// + private static DataSourceInfo LoadUsageStatsFromFileWithInfo(string filePath, string sourceName) + { + try + { + if (File.Exists(filePath)) + { + string json = File.ReadAllText(filePath); + var stats = JsonConvert.DeserializeObject(json); + if (stats != null && !string.IsNullOrEmpty(stats.DeviceId)) + { + var fileInfo = new FileInfo(filePath); + var dataSource = new DataSourceInfo + { + Stats = stats, + Source = sourceName, + LastModified = stats.LastModified != DateTime.MinValue ? stats.LastModified : fileInfo.LastWriteTime, + IsIntegrityValid = false, + TrustScore = 0 + }; + + // 验证数据完整性 + if (!string.IsNullOrEmpty(stats.DataHash)) + { + dataSource.IsIntegrityValid = stats.VerifyDataIntegrity(); + dataSource.TrustScore = dataSource.IsIntegrityValid ? 100 : 20; // 完整数据100分,篡改数据20分 + } + else + { + // 旧版本数据,中等信任度 + dataSource.IsIntegrityValid = true; + dataSource.TrustScore = 60; + stats.UpdateDataHash(); + } + + return dataSource; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从文件加载数据源信息失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error); + } + return null; + } + + /// + /// 收集所有可用的使用统计数据源 + /// + private static List CollectAllUsageDataSources() + { + var dataSources = new List(); + + try + { + // 1. 收集文件数据源 + var fileSources = new[] + { + new { Path = UsageStatsFilePath, Name = "主文件" }, + new { Path = BackupUsageStatsPath, Name = "第一备份" }, + new { Path = SecondaryUsageBackupPath, Name = "第二备份" }, + new { Path = TertiaryUsageBackupPath, Name = "第三备份" }, + new { Path = QuaternaryUsageBackupPath, Name = "第四备份" } + }; + + foreach (var source in fileSources) + { + var dataSource = LoadUsageStatsFromFileWithInfo(source.Path, source.Name); + if (dataSource != null) + { + dataSources.Add(dataSource); + } + } + + // 2. 收集注册表数据源 + var registrySource = LoadUsageStatsFromRegistryWithInfo(@"Software\ICC\DeviceInfo", "主注册表"); + if (registrySource != null) + { + dataSources.Add(registrySource); + } + + // 3. 收集备用注册表数据源 + var backupRegistryPaths = new[] + { + new { Path = @"Software\Microsoft\Windows\CurrentVersion\ICC", Name = "备用注册表1" }, + new { Path = @"Software\Classes\.icc\UsageData", Name = "备用注册表2" }, + new { Path = @"Software\ICC\Config\Usage", Name = "备用注册表3" } + }; + + foreach (var regPath in backupRegistryPaths) + { + var regSource = LoadUsageStatsFromBackupRegistryWithInfo(regPath.Path, regPath.Name); + if (regSource != null) + { + dataSources.Add(regSource); + } + } + + LogHelper.WriteLogToFile($"DeviceIdentifier | 收集到 {dataSources.Count} 个数据源"); + return dataSources; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 收集数据源失败: {ex.Message}", LogHelper.LogType.Error); + return dataSources; + } + } + + /// + /// 选择最可信的数据源 + /// + private static DataSourceInfo SelectMostTrustedData(List dataSources) + { + try + { + // 首先尝试找到完整性验证通过的数据 + var validSources = dataSources.Where(d => d.IsIntegrityValid).ToList(); + + if (validSources.Count > 0) + { + // 在有效数据中选择最新的 + var bestValid = validSources.OrderByDescending(d => d.LastModified).First(); + LogHelper.WriteLogToFile($"DeviceIdentifier | 选择完整性验证通过的最新数据: {bestValid.Source}"); + return bestValid; + } + + // 如果没有完整性验证通过的数据,选择信任度最高的 + var bestByTrust = dataSources.OrderByDescending(d => d.TrustScore).ThenByDescending(d => d.LastModified).FirstOrDefault(); + if (bestByTrust != null) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 选择信任度最高的数据: {bestByTrust.Source} (信任度: {bestByTrust.TrustScore})", LogHelper.LogType.Warning); + return bestByTrust; + } + + return null; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 选择最可信数据失败: {ex.Message}", LogHelper.LogType.Error); + return null; + } + } + + /// + /// 尝试从部分损坏的数据中恢复 + /// + private static UsageStats AttemptPartialDataRecovery(List dataSources) + { + try + { + if (dataSources.Count == 0) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 没有可用数据源进行部分恢复"); + return null; + } + + // 从所有数据源中提取可信的字段 + var recoveredStats = new UsageStats + { + DeviceId = DeviceId, + LastLaunchTime = DateTime.Now, + LaunchCount = 0, + TotalUsageMinutes = 0, + AverageSessionMinutes = 0, + LastUpdateCheck = DateTime.MinValue, + UpdatePriority = UpdatePriority.Medium, + UsageFrequency = UsageFrequency.Medium + }; + + // 使用多数投票或最大值策略恢复关键数据 + var launchCounts = dataSources.Where(d => d.Stats.LaunchCount > 0).Select(d => d.Stats.LaunchCount).ToList(); + var usageMinutes = dataSources.Where(d => d.Stats.TotalUsageMinutes > 0).Select(d => d.Stats.TotalUsageMinutes).ToList(); + + if (launchCounts.Count > 0) + { + recoveredStats.LaunchCount = (int)launchCounts.Average(); // 使用平均值 + } + + if (usageMinutes.Count > 0) + { + recoveredStats.TotalUsageMinutes = (long)usageMinutes.Average(); // 使用平均值 + } + + // 重新计算平均会话时长 + if (recoveredStats.LaunchCount > 0) + { + recoveredStats.AverageSessionMinutes = (double)recoveredStats.TotalUsageMinutes / recoveredStats.LaunchCount; + } + + // 重新计算使用频率 + CalculateUsageFrequency(recoveredStats); + + // 更新数据完整性哈希 + recoveredStats.UpdateDataHash(); + + LogHelper.WriteLogToFile($"DeviceIdentifier | 部分数据恢复完成 - 启动次数: {recoveredStats.LaunchCount}, 使用时长: {recoveredStats.TotalUsageMinutes}分钟"); + return recoveredStats; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 部分数据恢复失败: {ex.Message}", LogHelper.LogType.Error); + return null; + } + } + + /// + /// 从注册表加载使用统计(带数据源信息) + /// + private static DataSourceInfo LoadUsageStatsFromRegistryWithInfo(string registryPath, string sourceName) + { + try + { + using (var key = Registry.CurrentUser.OpenSubKey(registryPath)) + { + if (key != null) + { + var deviceId = key.GetValue("DeviceId") as string; + var launchCount = key.GetValue("LaunchCount"); + var totalMinutes = key.GetValue("TotalUsageMinutes"); + var lastLaunch = key.GetValue("LastLaunchTime") as string; + var priority = key.GetValue("UpdatePriority"); + var frequency = key.GetValue("UsageFrequency"); + var dataHash = key.GetValue("DataHash") as string; + var lastUpdate = key.GetValue("LastUpdate") as string; + + if (!string.IsNullOrEmpty(deviceId) && launchCount != null) + { + var stats = new UsageStats + { + DeviceId = deviceId, + LaunchCount = Convert.ToInt32(launchCount), + TotalUsageMinutes = totalMinutes != null ? Convert.ToInt64(totalMinutes) : 0, + LastLaunchTime = DateTime.TryParse(lastLaunch, out var dt) ? dt : DateTime.Now, + UpdatePriority = priority != null ? (UpdatePriority)Convert.ToInt32(priority) : UpdatePriority.Medium, + UsageFrequency = frequency != null ? (UsageFrequency)Convert.ToInt32(frequency) : UsageFrequency.Medium, + DataHash = dataHash, + AverageSessionMinutes = 0, + LastUpdateCheck = DateTime.MinValue + }; + + // 重新计算平均会话时长 + if (stats.LaunchCount > 0) + { + stats.AverageSessionMinutes = (double)stats.TotalUsageMinutes / stats.LaunchCount; + } + + var dataSource = new DataSourceInfo + { + Stats = stats, + Source = sourceName, + LastModified = DateTime.TryParse(lastUpdate, out var updateTime) ? updateTime : DateTime.Now, + IsIntegrityValid = false, + TrustScore = 80 // 注册表数据信任度较高 + }; + + // 验证数据完整性 + if (!string.IsNullOrEmpty(stats.DataHash)) + { + dataSource.IsIntegrityValid = stats.VerifyDataIntegrity(); + dataSource.TrustScore = dataSource.IsIntegrityValid ? 100 : 30; + } + + return dataSource; + } + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表加载数据源信息失败 ({registryPath}): {ex.Message}", LogHelper.LogType.Error); + } + return null; + } + + /// + /// 从备用注册表位置加载使用统计(带数据源信息) + /// + private static DataSourceInfo LoadUsageStatsFromBackupRegistryWithInfo(string registryPath, string sourceName) + { + try + { + using (var key = Registry.CurrentUser.OpenSubKey(registryPath)) + { + if (key != null) + { + var launchCount = key.GetValue("LC"); + var totalMinutes = key.GetValue("TUM"); + var lastLaunchBinary = key.GetValue("LLT"); + var priority = key.GetValue("UP"); + var frequency = key.GetValue("UF"); + var dataHash = key.GetValue("DH") as string; + var lastUpdateBinary = key.GetValue("LU"); + + if (launchCount != null && totalMinutes != null) + { + var stats = new UsageStats + { + DeviceId = DeviceId, + LaunchCount = Convert.ToInt32(launchCount), + TotalUsageMinutes = Convert.ToInt64(totalMinutes), + LastLaunchTime = lastLaunchBinary != null ? DateTime.FromBinary(Convert.ToInt64(lastLaunchBinary)) : DateTime.Now, + UpdatePriority = priority != null ? (UpdatePriority)Convert.ToInt32(priority) : UpdatePriority.Medium, + UsageFrequency = frequency != null ? (UsageFrequency)Convert.ToInt32(frequency) : UsageFrequency.Medium, + DataHash = dataHash, + AverageSessionMinutes = 0, + LastUpdateCheck = DateTime.MinValue + }; + + // 重新计算平均会话时长 + if (stats.LaunchCount > 0) + { + stats.AverageSessionMinutes = (double)stats.TotalUsageMinutes / stats.LaunchCount; + } + + var dataSource = new DataSourceInfo + { + Stats = stats, + Source = sourceName, + LastModified = lastUpdateBinary != null ? DateTime.FromBinary(Convert.ToInt64(lastUpdateBinary)) : DateTime.Now, + IsIntegrityValid = false, + TrustScore = 75 // 备用注册表数据信任度中等 + }; + + // 验证数据完整性 + if (!string.IsNullOrEmpty(stats.DataHash)) + { + dataSource.IsIntegrityValid = stats.VerifyDataIntegrity(); + dataSource.TrustScore = dataSource.IsIntegrityValid ? 100 : 25; + } + + return dataSource; + } + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从备用注册表加载数据源信息失败 ({registryPath}): {ex.Message}", LogHelper.LogType.Error); + } + return null; + } + + /// + /// 从注册表加载使用统计(保持向后兼容) + /// + private static UsageStats LoadUsageStatsFromRegistry() + { + var dataSource = LoadUsageStatsFromRegistryWithInfo(@"Software\ICC\DeviceInfo", "主注册表"); + return dataSource?.Stats; + } + + /// + /// 从多个注册表位置加载使用统计(强化恢复) + /// + private static UsageStats LoadUsageStatsFromMultipleRegistryLocations() + { + var registryPaths = new[] + { + @"Software\Microsoft\Windows\CurrentVersion\ICC", + @"Software\Classes\.icc\UsageData", + @"Software\ICC\Config\Usage" + }; + + foreach (var path in registryPaths) + { + try + { + using (var key = Registry.CurrentUser.OpenSubKey(path)) + { + if (key != null) + { + var launchCount = key.GetValue("LC"); + var totalMinutes = key.GetValue("TUM"); + var lastLaunchBinary = key.GetValue("LLT"); + var priority = key.GetValue("UP"); + var frequency = key.GetValue("UF"); + var dataHash = key.GetValue("DH") as string; + + if (launchCount != null && totalMinutes != null) + { + var stats = new UsageStats + { + DeviceId = DeviceId, + LaunchCount = Convert.ToInt32(launchCount), + TotalUsageMinutes = Convert.ToInt64(totalMinutes), + LastLaunchTime = lastLaunchBinary != null ? DateTime.FromBinary(Convert.ToInt64(lastLaunchBinary)) : DateTime.Now, + UpdatePriority = priority != null ? (UpdatePriority)Convert.ToInt32(priority) : UpdatePriority.Medium, + UsageFrequency = frequency != null ? (UsageFrequency)Convert.ToInt32(frequency) : UsageFrequency.Medium, + DataHash = dataHash, + AverageSessionMinutes = 0, + LastUpdateCheck = DateTime.MinValue + }; + + // 重新计算平均会话时长 + if (stats.LaunchCount > 0) + { + stats.AverageSessionMinutes = (double)stats.TotalUsageMinutes / stats.LaunchCount; + } + + // 验证数据完整性(如果有哈希值) + if (!string.IsNullOrEmpty(stats.DataHash)) + { + if (stats.VerifyDataIntegrity()) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表位置恢复数据并验证完整性通过: {path}"); + return stats; + } + else + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 注册表位置数据完整性验证失败: {path}", LogHelper.LogType.Warning); + continue; // 尝试下一个位置 + } + } + else + { + // 没有哈希值的旧数据,更新哈希后返回 + stats.UpdateDataHash(); + LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表位置恢复旧版本数据: {path}"); + return stats; + } + } + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表位置加载失败 ({path}): {ex.Message}", LogHelper.LogType.Error); + } + } + + return null; + } + + /// + /// 保存使用统计到所有位置(强化版本 - 多重隐藏备份) + /// + private static void SaveUsageStatsToAllLocations(UsageStats stats) + { + // 保存到主文件 + SaveUsageStatsToFile(UsageStatsFilePath, stats); + + // 保存到第一备份文件 + SaveUsageStatsToFile(BackupUsageStatsPath, stats); + + // 保存到多个隐藏备份位置(专门针对使用频率数据保护) + SaveUsageStatsToFile(SecondaryUsageBackupPath, stats); + SaveUsageStatsToFile(TertiaryUsageBackupPath, stats); + SaveUsageStatsToFile(QuaternaryUsageBackupPath, stats); + + // 保存到注册表 + SaveUsageStatsToRegistry(stats); + + // 保存到注册表的多个位置 + SaveUsageStatsToMultipleRegistryLocations(stats); + } + + /// + /// 保存使用统计到文件 + /// + private static void SaveUsageStatsToFile(string filePath, UsageStats stats) + { + try + { + // 确保目录存在 + var directory = Path.GetDirectoryName(filePath); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + // 设置隐藏属性 + if (filePath.Contains(".sys")) + { + var dirInfo = new DirectoryInfo(directory); + dirInfo.Attributes |= FileAttributes.Hidden | FileAttributes.System; + } + } + + string json = JsonConvert.SerializeObject(stats, Formatting.Indented); + File.WriteAllText(filePath, json); + + // 设置文件属性为隐藏和系统文件 + if (filePath.Contains(".sys")) + { + var fileInfo = new FileInfo(filePath); + fileInfo.Attributes |= FileAttributes.Hidden | FileAttributes.System; + } + + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用统计已保存到: {filePath}"); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 保存使用统计到文件失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 保存使用统计到注册表 + /// + private static void SaveUsageStatsToRegistry(UsageStats stats) + { + try + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 开始保存使用统计到主注册表位置"); + + using (var key = Registry.CurrentUser.CreateSubKey(@"Software\ICC\DeviceInfo")) + { + if (key != null) + { + key.SetValue("DeviceId", stats.DeviceId); + key.SetValue("LaunchCount", stats.LaunchCount); + key.SetValue("TotalUsageMinutes", stats.TotalUsageMinutes); + key.SetValue("LastLaunchTime", stats.LastLaunchTime.ToString("yyyy-MM-dd HH:mm:ss")); + key.SetValue("UpdatePriority", (int)stats.UpdatePriority); + key.SetValue("UsageFrequency", (int)stats.UsageFrequency); + key.SetValue("DataHash", stats.DataHash ?? ""); + key.SetValue("LastUpdate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用统计已保存到主注册表 - 启动次数: {stats.LaunchCount}, 使用时长: {stats.TotalUsageMinutes}分钟"); + } + else + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 创建主注册表键失败", LogHelper.LogType.Error); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 保存使用统计到主注册表失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 保存使用统计到多个注册表位置(强化保护) + /// + private static void SaveUsageStatsToMultipleRegistryLocations(UsageStats stats) + { + var registryPaths = new[] + { + @"Software\Microsoft\Windows\CurrentVersion\ICC", + @"Software\Classes\.icc\UsageData", + @"Software\ICC\Config\Usage" + }; + + LogHelper.WriteLogToFile($"DeviceIdentifier | 开始保存使用统计到{registryPaths.Length}个备用注册表位置"); + var successCount = 0; + + foreach (var path in registryPaths) + { + try + { + using (var key = Registry.CurrentUser.CreateSubKey(path)) + { + if (key != null) + { + // 使用编码的键名来隐藏数据 + key.SetValue("LC", stats.LaunchCount); // LaunchCount + key.SetValue("TUM", stats.TotalUsageMinutes); // TotalUsageMinutes + key.SetValue("LLT", stats.LastLaunchTime.ToBinary()); // LastLaunchTime + key.SetValue("UP", (int)stats.UpdatePriority); // UpdatePriority + key.SetValue("UF", (int)stats.UsageFrequency); // UsageFrequency + key.SetValue("DH", stats.DataHash ?? ""); // DataHash + key.SetValue("LU", DateTime.Now.ToBinary()); // LastUpdate + + successCount++; + LogHelper.WriteLogToFile($"DeviceIdentifier | 成功保存到备用注册表位置: {path}"); + } + else + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 创建备用注册表键失败: {path}", LogHelper.LogType.Error); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 保存到备用注册表位置失败 ({path}): {ex.Message}", LogHelper.LogType.Error); + } + } + + LogHelper.WriteLogToFile($"DeviceIdentifier | 备用注册表保存完成: {successCount}/{registryPaths.Length} 成功"); + } + + /// + /// 记录更新检查时间(同时执行数据保护检查) + /// + public static void RecordUpdateCheck() + { + try + { + lock (fileLock) + { + var stats = LoadUsageStats(); + stats.LastUpdateCheck = DateTime.Now; + stats.UpdateDataHash(); + SaveUsageStats(stats); + + // 定期执行数据保护检查(每10次更新检查执行一次) + if (stats.LaunchCount % 10 == 0) + { + PerformUsageDataProtectionCheck(); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 记录更新检查失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 执行使用频率数据保护检查和自动修复 + /// + public static bool PerformUsageDataProtectionCheck() + { + try + { + lock (fileLock) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 开始使用频率数据保护检查"); + + var issues = new List(); + var repaired = new List(); + var backupPaths = new[] + { + new { Path = UsageStatsFilePath, Name = "主文件" }, + new { Path = BackupUsageStatsPath, Name = "第一备份" }, + new { Path = SecondaryUsageBackupPath, Name = "第二备份" }, + new { Path = TertiaryUsageBackupPath, Name = "第三备份" }, + new { Path = QuaternaryUsageBackupPath, Name = "第四备份" } + }; + + // 检查所有备份文件 + UsageStats validStats = null; + var missingFiles = new List(); + + foreach (var backup in backupPaths) + { + if (!File.Exists(backup.Path)) + { + issues.Add($"{backup.Name}丢失"); + missingFiles.Add(backup.Path); + } + else + { + var stats = LoadUsageStatsFromFile(backup.Path); + if (stats != null && stats.VerifyDataIntegrity() && validStats == null) + { + validStats = stats; + } + } + } + + // 如果找到有效数据,修复丢失的文件 + if (validStats != null && missingFiles.Count > 0) + { + foreach (var missingFile in missingFiles) + { + SaveUsageStatsToFile(missingFile, validStats); + repaired.Add($"恢复文件: {Path.GetFileName(missingFile)}"); + } + } + + // 检查注册表备份 + var registryPaths = new[] + { + @"Software\ICC\DeviceInfo", + @"Software\Microsoft\Windows\CurrentVersion\ICC", + @"Software\Classes\.icc\UsageData", + @"Software\ICC\Config\Usage" + }; + + var missingRegistryBackups = 0; + foreach (var path in registryPaths) + { + try + { + using (var key = Registry.CurrentUser.OpenSubKey(path)) + { + if (key == null) missingRegistryBackups++; + } + } + catch + { + missingRegistryBackups++; + } + } + + if (missingRegistryBackups > 0) + { + issues.Add($"{missingRegistryBackups}个注册表备份丢失"); + if (validStats != null) + { + SaveUsageStatsToMultipleRegistryLocations(validStats); + repaired.Add("重建注册表备份"); + } + } + + // 如果没有找到任何有效数据,尝试从注册表恢复 + if (validStats == null) + { + validStats = LoadUsageStatsFromRegistry(); + if (validStats == null) + { + validStats = LoadUsageStatsFromMultipleRegistryLocations(); + } + + if (validStats != null) + { + SaveUsageStatsToAllLocations(validStats); + repaired.Add("从注册表完全恢复数据"); + } + else + { + // 最后手段:强制重建 + ForceRebuildUsageDataBackups(); + repaired.Add("强制重建所有备份"); + } + } + + // 记录检查结果 + if (issues.Count > 0) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据保护检查发现问题: {string.Join(", ", issues)}", LogHelper.LogType.Warning); + } + + if (repaired.Count > 0) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据保护修复: {string.Join(", ", repaired)}"); + } + + var protectionScore = CalculateUsageDataProtectionScore(); + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据保护检查完成 - 问题: {issues.Count}, 修复: {repaired.Count}, 保护强度: {protectionScore}/100"); + + return protectionScore >= 80; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据保护检查失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + /// + /// 根据优先级决定是否应该推送更新(综合时间和使用频率判断) + /// + /// 更新版本号 + /// 发布时间 + /// 是否应该推送更新 + public static bool ShouldPushUpdate(string updateVersion, DateTime releaseTime) + { + try + { + var priority = GetUpdatePriority(); + var frequency = GetUsageFrequency(); + var stats = LoadUsageStats(); + + // 计算发布时间到现在的天数 + var daysSinceRelease = (DateTime.Now - releaseTime).TotalDays; + + // 计算最近活跃度(最后一次使用距今的天数) + var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays; + + // 判断更新类型(基于版本号) + var updateType = DetermineUpdateType(updateVersion); + + // 综合判断逻辑 + var shouldPush = ShouldPushUpdateComprehensive(priority, frequency, daysSinceRelease, daysSinceLastUse, stats, updateType); + + LogHelper.WriteLogToFile($"DeviceIdentifier | 更新推送判断 - 版本: {updateVersion}, 类型: {updateType}, " + + $"优先级: {priority}, 频率: {frequency}, 发布天数: {daysSinceRelease:F1}, " + + $"最后使用: {daysSinceLastUse:F1}天前, 结果: {shouldPush}"); + + return shouldPush; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 判断是否推送更新失败: {ex.Message}", LogHelper.LogType.Error); + return true; // 出错时默认推送 + } + } + + /// + /// 更新类型枚举 + /// + private enum UpdateType + { + Major, // 主版本更新 (x.0.0) + Minor, // 次版本更新 (x.y.0) + Patch, // 补丁更新 (x.y.z) + Hotfix, // 热修复更新 + Unknown // 未知类型 + } + + /// + /// 根据版本号判断更新类型 + /// + private static UpdateType DetermineUpdateType(string version) + { + if (string.IsNullOrEmpty(version)) return UpdateType.Unknown; + + try + { + // 移除可能的前缀(如 "v") + var cleanVersion = version.TrimStart('v', 'V'); + + // 检查是否包含热修复标识 + if (cleanVersion.ToLower().Contains("hotfix") || cleanVersion.ToLower().Contains("fix")) + { + return UpdateType.Hotfix; + } + + // 解析版本号 + var parts = cleanVersion.Split('.'); + if (parts.Length >= 3) + { + if (int.TryParse(parts[1], out int minor) && int.TryParse(parts[2], out int patch)) + { + if (minor == 0 && patch == 0) return UpdateType.Major; + if (patch == 0) return UpdateType.Minor; + return UpdateType.Patch; + } + } + + return UpdateType.Unknown; + } + catch + { + return UpdateType.Unknown; + } + } + + /// + /// 综合时间和使用频率的更新推送判断逻辑 + /// + private static bool ShouldPushUpdateComprehensive(UpdatePriority priority, UsageFrequency frequency, + double daysSinceRelease, double daysSinceLastUse, UsageStats stats, UpdateType updateType) + { + // 考虑用户的总体使用模式 + var isHeavyUser = stats.TotalUsageMinutes > 3000; // 超过50小时的重度用户 + var isFrequentUser = stats.LaunchCount > 100; // 启动超过100次的频繁用户 + + // 根据更新类型调整推送策略 + var urgencyMultiplier = GetUpdateUrgencyMultiplier(updateType); + + // 如果用户长时间未使用(超过30天),降低推送优先级 + if (daysSinceLastUse > 30) + { + // 热修复和重要更新优先推送 + if (updateType == UpdateType.Hotfix) + { + return daysSinceRelease >= 1; // 热修复1天后推送 + } + + // 但如果是重度用户,仍然要适当推送 + var baseDelay = isHeavyUser ? 7 : 14; + return daysSinceRelease >= (baseDelay / urgencyMultiplier); + } + + // 如果用户最近很活跃(3天内使用过) + if (daysSinceLastUse <= 3) + { + // 热修复立即推送给活跃用户 + if (updateType == UpdateType.Hotfix) + { + return true; + } + + // 结合使用频率和优先级判断 + if (frequency == UsageFrequency.High || isHeavyUser) + { + return daysSinceRelease >= Math.Max(0, 1 / urgencyMultiplier); // 高频用户优先推送 + } + + switch (priority) + { + case UpdatePriority.High: + return daysSinceRelease >= Math.Max(0, 1 / urgencyMultiplier); + + case UpdatePriority.Medium: + return daysSinceRelease >= Math.Max(1, 2 / urgencyMultiplier); + + case UpdatePriority.Low: + return daysSinceRelease >= Math.Max(2, 3 / urgencyMultiplier); + } + } + + // 中等活跃度用户(3-14天内使用过) + if (daysSinceLastUse <= 14) + { + // 热修复优先推送 + if (updateType == UpdateType.Hotfix) + { + return daysSinceRelease >= 1; + } + + // 频繁用户优先推送 + if (isFrequentUser && frequency == UsageFrequency.High) + { + return daysSinceRelease >= Math.Max(1, 2 / urgencyMultiplier); + } + + switch (priority) + { + case UpdatePriority.High: + return daysSinceRelease >= Math.Max(1, 2 / urgencyMultiplier); + + case UpdatePriority.Medium: + return daysSinceRelease >= Math.Max(2, 4 / urgencyMultiplier); + + case UpdatePriority.Low: + return daysSinceRelease >= Math.Max(4, 7 / urgencyMultiplier); + } + } + + // 较不活跃用户(14-30天内使用过) + // 对于低频率用户,进一步延迟推送 + var delayMultiplier = frequency == UsageFrequency.Low ? 2 : 1; + + switch (priority) + { + case UpdatePriority.High: + return daysSinceRelease >= Math.Max(2, 3 * delayMultiplier / urgencyMultiplier); + + case UpdatePriority.Medium: + return daysSinceRelease >= Math.Max(4, 7 * delayMultiplier / urgencyMultiplier); + + case UpdatePriority.Low: + return daysSinceRelease >= Math.Max(7, 14 * delayMultiplier / urgencyMultiplier); + + default: + return daysSinceRelease >= 7; + } + } + + /// + /// 根据更新类型获取紧急程度倍数 + /// + private static double GetUpdateUrgencyMultiplier(UpdateType updateType) + { + switch (updateType) + { + case UpdateType.Hotfix: + return 3.0; // 热修复最紧急,3倍速度推送 + case UpdateType.Major: + return 0.5; // 主版本更新较慢推送 + case UpdateType.Minor: + return 1.0; // 次版本正常推送 + case UpdateType.Patch: + return 1.5; // 补丁更新稍快推送 + case UpdateType.Unknown: + return 1.0; // 未知类型正常推送 + default: + return 1.0; + } + } + + /// + /// 获取设备信息摘要(用于调试) + /// + public static string GetDeviceInfoSummary() + { + try + { + var (launchCount, totalMinutes, avgSession, priority) = GetUsageStats(); + var frequency = GetUsageFrequency(); + var stats = LoadUsageStats(); + var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays; + + return $"设备ID: {DeviceId}\n" + + $"启动次数: {launchCount}\n" + + $"总使用时长: {totalMinutes}分钟 ({totalMinutes / 60.0:F1}小时)\n" + + $"平均会话时长: {avgSession:F1}分钟\n" + + $"使用频率: {frequency}\n" + + $"更新优先级: {priority}\n" + + $"最后使用: {daysSinceLastUse:F1}天前\n" + + $"用户类型: {GetUserTypeDescription(stats)}"; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 获取设备信息摘要失败: {ex.Message}", LogHelper.LogType.Error); + return $"设备ID: {DeviceId}\n获取详细信息失败"; + } + } + + /// + /// 获取用户类型描述 + /// + private static string GetUserTypeDescription(UsageStats stats) + { + var isHeavyUser = stats.TotalUsageMinutes > 3000; + var isFrequentUser = stats.LaunchCount > 100; + var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays; + + var descriptions = new List(); + + if (isHeavyUser) descriptions.Add("重度用户"); + if (isFrequentUser) descriptions.Add("频繁用户"); + + if (daysSinceLastUse <= 3) descriptions.Add("高活跃"); + else if (daysSinceLastUse <= 14) descriptions.Add("中活跃"); + else if (daysSinceLastUse <= 30) descriptions.Add("低活跃"); + else descriptions.Add("非活跃"); + + return descriptions.Count > 0 ? string.Join(", ", descriptions) : "普通用户"; + } + + /// + /// 模拟更新推送判断(用于测试) + /// + /// 更新版本号 + /// 发布时间 + /// 推送判断详情 + public static string SimulateUpdatePushDecision(string updateVersion, DateTime releaseTime) + { + try + { + var priority = GetUpdatePriority(); + var frequency = GetUsageFrequency(); + var stats = LoadUsageStats(); + var daysSinceRelease = (DateTime.Now - releaseTime).TotalDays; + var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays; + var updateType = DetermineUpdateType(updateVersion); + var urgencyMultiplier = GetUpdateUrgencyMultiplier(updateType); + var shouldPush = ShouldPushUpdate(updateVersion, releaseTime); + + return $"更新推送判断详情:\n" + + $"版本号: {updateVersion}\n" + + $"更新类型: {updateType}\n" + + $"发布时间: {releaseTime:yyyy-MM-dd HH:mm}\n" + + $"发布天数: {daysSinceRelease:F1}天\n" + + $"最后使用: {daysSinceLastUse:F1}天前\n" + + $"使用频率: {frequency}\n" + + $"更新优先级: {priority}\n" + + $"紧急程度倍数: {urgencyMultiplier:F1}x\n" + + $"用户类型: {GetUserTypeDescription(stats)}\n" + + $"推送决定: {(shouldPush ? "是" : "否")}"; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 模拟更新推送判断失败: {ex.Message}", LogHelper.LogType.Error); + return "模拟判断失败"; + } + } + + /// + /// 数据自检和修复 + /// + public static bool PerformDataIntegrityCheck() + { + try + { + lock (fileLock) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 开始数据完整性检查"); + + var issues = new List(); + var repaired = new List(); + + // 检查设备ID文件 + if (!File.Exists(DeviceIdFilePath)) + { + issues.Add("主设备ID文件丢失"); + if (File.Exists(BackupDeviceIdPath)) + { + var backupId = LoadDeviceIdFromFile(BackupDeviceIdPath); + if (!string.IsNullOrEmpty(backupId)) + { + SaveDeviceIdToFile(DeviceIdFilePath, backupId); + repaired.Add("从备份恢复主设备ID文件"); + } + } + } + + // 检查备份设备ID文件 + if (!File.Exists(BackupDeviceIdPath)) + { + issues.Add("备份设备ID文件丢失"); + var mainId = LoadDeviceIdFromFile(DeviceIdFilePath); + if (!string.IsNullOrEmpty(mainId)) + { + SaveDeviceIdToFile(BackupDeviceIdPath, mainId); + repaired.Add("重建备份设备ID文件"); + } + } + + // 检查使用统计文件 + if (!File.Exists(UsageStatsFilePath)) + { + issues.Add("主使用统计文件丢失"); + var backupStatsForRestore = LoadUsageStatsFromFile(BackupUsageStatsPath); + if (backupStatsForRestore != null) + { + SaveUsageStatsToFile(UsageStatsFilePath, backupStatsForRestore); + repaired.Add("从备份恢复主使用统计文件"); + } + } + + // 检查备份使用统计文件 + if (!File.Exists(BackupUsageStatsPath)) + { + issues.Add("备份使用统计文件丢失"); + var mainStatsForBackup = LoadUsageStatsFromFile(UsageStatsFilePath); + if (mainStatsForBackup != null) + { + SaveUsageStatsToFile(BackupUsageStatsPath, mainStatsForBackup); + repaired.Add("重建备份使用统计文件"); + } + } + + // 验证数据一致性 + var mainStats = LoadUsageStatsFromFile(UsageStatsFilePath); + var backupStats = LoadUsageStatsFromFile(BackupUsageStatsPath); + + if (mainStats != null && backupStats != null) + { + if (mainStats.LaunchCount != backupStats.LaunchCount || + mainStats.TotalUsageMinutes != backupStats.TotalUsageMinutes) + { + issues.Add("主备份数据不一致"); + // 使用最新的数据 + var newerStats = mainStats.LastModified > backupStats.LastModified ? mainStats : backupStats; + SaveUsageStatsToAllLocations(newerStats); + repaired.Add("同步主备份数据"); + } + } + + // 记录检查结果 + if (issues.Count > 0) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 发现问题: {string.Join(", ", issues)}", LogHelper.LogType.Warning); + } + + if (repaired.Count > 0) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 已修复: {string.Join(", ", repaired)}"); + } + + LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性检查完成 - 问题: {issues.Count}, 修复: {repaired.Count}"); + return issues.Count == 0 || repaired.Count > 0; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性检查失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + /// + /// 获取使用频率数据保护状态摘要(强化版本) + /// + public static string GetUsageDataProtectionSummary() + { + try + { + var summary = new StringBuilder(); + summary.AppendLine("使用频率数据保护状态摘要:"); + + // 检查主要文件 + summary.AppendLine($"主使用统计文件: {(File.Exists(UsageStatsFilePath) ? "✓" : "✗")}"); + summary.AppendLine($"第一备份文件: {(File.Exists(BackupUsageStatsPath) ? "✓" : "✗")}"); + + // 检查多重隐藏备份 + summary.AppendLine($"第二备份文件: {(File.Exists(SecondaryUsageBackupPath) ? "✓" : "✗")}"); + summary.AppendLine($"第三备份文件: {(File.Exists(TertiaryUsageBackupPath) ? "✓" : "✗")}"); + summary.AppendLine($"第四备份文件: {(File.Exists(QuaternaryUsageBackupPath) ? "✓" : "✗")}"); + + // 检查注册表备份 + var registryBackups = 0; + var registryPaths = new[] + { + @"Software\ICC\DeviceInfo", + @"Software\Microsoft\Windows\CurrentVersion\ICC", + @"Software\Classes\.icc\UsageData", + @"Software\ICC\Config\Usage" + }; + + foreach (var path in registryPaths) + { + try + { + using (var key = Registry.CurrentUser.OpenSubKey(path)) + { + if (key != null) registryBackups++; + } + } + catch { } + } + + summary.AppendLine($"注册表备份位置: {registryBackups}/4 ✓"); + + // 检查数据完整性和可恢复性 + var stats = LoadUsageStats(); + if (stats != null) + { + summary.AppendLine($"数据完整性: {(stats.VerifyDataIntegrity() ? "✓" : "✗")}"); + summary.AppendLine($"启动次数: {stats.LaunchCount}"); + summary.AppendLine($"总使用时长: {stats.TotalUsageMinutes}分钟"); + summary.AppendLine($"使用频率: {stats.UsageFrequency}"); + summary.AppendLine($"更新优先级: {stats.UpdatePriority}"); + summary.AppendLine($"最后修改: {stats.LastModified:yyyy-MM-dd HH:mm:ss}"); + } + + // 计算保护强度评分 + var protectionScore = CalculateUsageDataProtectionScore(); + summary.AppendLine($"保护强度评分: {protectionScore}/100"); + + return summary.ToString(); + } + catch (Exception ex) + { + return $"获取使用频率数据保护状态失败: {ex.Message}"; + } + } + + /// + /// 计算使用频率数据保护强度评分 + /// + private static int CalculateUsageDataProtectionScore() + { + var score = 0; + + try + { + // 文件备份评分(50分) + if (File.Exists(UsageStatsFilePath)) score += 15; + if (File.Exists(BackupUsageStatsPath)) score += 10; + if (File.Exists(SecondaryUsageBackupPath)) score += 8; + if (File.Exists(TertiaryUsageBackupPath)) score += 8; + if (File.Exists(QuaternaryUsageBackupPath)) score += 9; + + // 注册表备份评分(30分) + var registryPaths = new[] + { + @"Software\ICC\DeviceInfo", + @"Software\Microsoft\Windows\CurrentVersion\ICC", + @"Software\Classes\.icc\UsageData", + @"Software\ICC\Config\Usage" + }; + + foreach (var path in registryPaths) + { + try + { + using (var key = Registry.CurrentUser.OpenSubKey(path)) + { + if (key != null) score += 7; + } + } + catch { } + } + + // 数据完整性评分(20分) + var stats = LoadUsageStats(); + if (stats != null) + { + if (!string.IsNullOrEmpty(stats.DataHash)) score += 10; + if (stats.VerifyDataIntegrity()) score += 10; + } + } + catch { } + + return Math.Min(100, score); + } + + /// + /// 强制重建所有使用频率数据备份 + /// + public static bool ForceRebuildUsageDataBackups() + { + try + { + lock (fileLock) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 开始强制重建使用频率数据备份"); + + var stats = LoadUsageStats(); + if (stats == null) + { + // 如果无法加载任何数据,创建基础数据 + stats = new UsageStats + { + DeviceId = DeviceId, + LastLaunchTime = DateTime.Now, + LaunchCount = 1, + TotalUsageMinutes = 0, + AverageSessionMinutes = 0, + LastUpdateCheck = DateTime.MinValue, + UpdatePriority = UpdatePriority.Medium, + UsageFrequency = UsageFrequency.Medium + }; + stats.UpdateDataHash(); + LogHelper.WriteLogToFile($"DeviceIdentifier | 创建新的基础使用数据"); + } + + // 强制保存到所有位置 + SaveUsageStatsToAllLocations(stats); + + // 验证重建结果 + var protectionScore = CalculateUsageDataProtectionScore(); + LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据备份重建完成,保护强度: {protectionScore}/100"); + + return protectionScore >= 80; // 80分以上认为重建成功 + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 强制重建使用频率数据备份失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + /// + /// 获取数据保护状态摘要(保持向后兼容) + /// + public static string GetDataProtectionSummary() + { + return GetUsageDataProtectionSummary(); + } + + /// + /// 测试注册表写入操作 + /// + public static string TestRegistryOperations() + { + var results = new StringBuilder(); + results.AppendLine("注册表操作测试结果:"); + + try + { + // 测试设备ID写入 + var testDeviceId = "TEST" + DateTime.Now.ToString("yyyyMMddHHmmss"); + results.AppendLine($"测试设备ID: {testDeviceId}"); + + try + { + using (var key = Registry.CurrentUser.CreateSubKey(@"Software\ICC\DeviceInfo")) + { + if (key != null) + { + key.SetValue("TestDeviceId", testDeviceId); + key.SetValue("TestTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + results.AppendLine("✓ 主注册表位置写入成功"); + } + else + { + results.AppendLine("✗ 主注册表位置创建失败"); + } + } + } + catch (Exception ex) + { + results.AppendLine($"✗ 主注册表位置写入失败: {ex.Message}"); + } + + // 测试多个注册表位置写入 + var registryPaths = new[] + { + @"Software\Microsoft\Windows\CurrentVersion\ICC", + @"Software\Classes\.icc\UsageData", + @"Software\ICC\Config\Usage" + }; + + foreach (var path in registryPaths) + { + try + { + using (var key = Registry.CurrentUser.CreateSubKey(path)) + { + if (key != null) + { + key.SetValue("TestData", "TestValue"); + key.SetValue("TestTime", DateTime.Now.ToBinary()); + results.AppendLine($"✓ 注册表位置写入成功: {path}"); + } + else + { + results.AppendLine($"✗ 注册表位置创建失败: {path}"); + } + } + } + catch (Exception ex) + { + results.AppendLine($"✗ 注册表位置写入失败 ({path}): {ex.Message}"); + } + } + + // 测试读取操作 + results.AppendLine("\n注册表读取测试:"); + try + { + using (var key = Registry.CurrentUser.OpenSubKey(@"Software\ICC\DeviceInfo")) + { + if (key != null) + { + var readValue = key.GetValue("TestDeviceId") as string; + if (readValue == testDeviceId) + { + results.AppendLine("✓ 注册表读取验证成功"); + } + else + { + results.AppendLine($"✗ 注册表读取验证失败: 期望 {testDeviceId}, 实际 {readValue}"); + } + } + else + { + results.AppendLine("✗ 注册表读取失败: 键不存在"); + } + } + } + catch (Exception ex) + { + results.AppendLine($"✗ 注册表读取失败: {ex.Message}"); + } + + // 测试实际数据保存 + results.AppendLine("\n实际数据保存测试:"); + var stats = LoadUsageStats(); + if (stats != null) + { + try + { + SaveUsageStatsToRegistry(stats); + SaveUsageStatsToMultipleRegistryLocations(stats); + results.AppendLine("✓ 实际使用统计数据保存到注册表成功"); + } + catch (Exception ex) + { + results.AppendLine($"✗ 实际使用统计数据保存失败: {ex.Message}"); + } + } + + return results.ToString(); + } + catch (Exception ex) + { + return $"注册表操作测试失败: {ex.Message}"; + } + } + + /// + /// 强制执行一次完整的数据保存操作(包括注册表) + /// + public static bool ForceCompleteDataSave() + { + try + { + lock (fileLock) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 开始强制完整数据保存"); + + // 保存设备ID到所有位置 + SaveDeviceIdToAllLocations(DeviceId); + + // 加载并保存使用统计到所有位置 + var stats = LoadUsageStats(); + if (stats != null) + { + stats.UpdateDataHash(); + SaveUsageStatsToAllLocations(stats); + + // 验证注册表保存是否成功 + var verificationResult = VerifyRegistryData(); + LogHelper.WriteLogToFile($"DeviceIdentifier | 注册表数据验证结果: {verificationResult}"); + + LogHelper.WriteLogToFile($"DeviceIdentifier | 强制完整数据保存完成"); + return true; + } + else + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 强制完整数据保存失败: 无法加载使用统计", LogHelper.LogType.Error); + return false; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 强制完整数据保存失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + /// + /// 验证注册表中的数据是否存在 + /// + public static string VerifyRegistryData() + { + var results = new StringBuilder(); + results.AppendLine("注册表数据验证结果:"); + + try + { + // 验证主注册表位置 + try + { + using (var key = Registry.CurrentUser.OpenSubKey(@"Software\ICC\DeviceInfo")) + { + if (key != null) + { + var deviceId = key.GetValue("DeviceId") as string; + var launchCount = key.GetValue("LaunchCount"); + var totalMinutes = key.GetValue("TotalUsageMinutes"); + var lastUpdate = key.GetValue("LastUpdate") as string; + + results.AppendLine($"✓ 主注册表位置存在"); + results.AppendLine($" 设备ID: {deviceId ?? "未找到"}"); + results.AppendLine($" 启动次数: {launchCount ?? "未找到"}"); + results.AppendLine($" 使用时长: {totalMinutes ?? "未找到"}分钟"); + results.AppendLine($" 最后更新: {lastUpdate ?? "未找到"}"); + } + else + { + results.AppendLine("✗ 主注册表位置不存在"); + } + } + } + catch (Exception ex) + { + results.AppendLine($"✗ 主注册表位置访问失败: {ex.Message}"); + } + + // 验证备用注册表位置 + var registryPaths = new[] + { + @"Software\Microsoft\Windows\CurrentVersion\ICC", + @"Software\Classes\.icc\UsageData", + @"Software\ICC\Config\Usage" + }; + + foreach (var path in registryPaths) + { + try + { + using (var key = Registry.CurrentUser.OpenSubKey(path)) + { + if (key != null) + { + var launchCount = key.GetValue("LC"); + var totalMinutes = key.GetValue("TUM"); + var lastUpdate = key.GetValue("LU"); + + results.AppendLine($"✓ 备用注册表位置存在: {path}"); + results.AppendLine($" 启动次数: {launchCount ?? "未找到"}"); + results.AppendLine($" 使用时长: {totalMinutes ?? "未找到"}"); + results.AppendLine($" 最后更新: {(lastUpdate != null ? DateTime.FromBinary(Convert.ToInt64(lastUpdate)).ToString("yyyy-MM-dd HH:mm:ss") : "未找到")}"); + } + else + { + results.AppendLine($"✗ 备用注册表位置不存在: {path}"); + } + } + } + catch (Exception ex) + { + results.AppendLine($"✗ 备用注册表位置访问失败 ({path}): {ex.Message}"); + } + } + + return results.ToString(); + } + catch (Exception ex) + { + return $"注册表数据验证失败: {ex.Message}"; + } + } + + /// + /// 立即执行一次数据保存并验证注册表写入 + /// + public static string SaveAndVerifyRegistryData() + { + try + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 开始保存并验证注册表数据"); + + // 强制保存数据 + var saveSuccess = ForceCompleteDataSave(); + + // 验证注册表数据 + var verificationResult = VerifyRegistryData(); + + var result = $"保存操作: {(saveSuccess ? "成功" : "失败")}\n\n{verificationResult}"; + + LogHelper.WriteLogToFile($"DeviceIdentifier | 保存并验证注册表数据完成"); + return result; + } + catch (Exception ex) + { + var errorMsg = $"保存并验证注册表数据失败: {ex.Message}"; + LogHelper.WriteLogToFile($"DeviceIdentifier | {errorMsg}", LogHelper.LogType.Error); + return errorMsg; + } + } + + /// + /// 测试反篡改恢复机制 + /// + public static string TestAntiTamperingRecovery() + { + var results = new StringBuilder(); + results.AppendLine("反篡改恢复机制测试结果:"); + + try + { + lock (fileLock) + { + // 1. 收集所有数据源 + results.AppendLine("\n1. 收集数据源:"); + var allSources = CollectAllUsageDataSources(); + results.AppendLine($" 找到 {allSources.Count} 个数据源"); + + foreach (var source in allSources) + { + results.AppendLine($" - {source.Source}: 完整性={source.IsIntegrityValid}, 信任度={source.TrustScore}, 修改时间={source.LastModified:yyyy-MM-dd HH:mm:ss}"); + results.AppendLine($" 启动次数={source.Stats.LaunchCount}, 使用时长={source.Stats.TotalUsageMinutes}分钟"); + } + + // 2. 选择最可信数据 + results.AppendLine("\n2. 选择最可信数据:"); + var bestData = SelectMostTrustedData(allSources); + if (bestData != null) + { + results.AppendLine($" 选择: {bestData.Source}"); + results.AppendLine($" 完整性: {bestData.IsIntegrityValid}"); + results.AppendLine($" 信任度: {bestData.TrustScore}"); + results.AppendLine($" 数据: 启动{bestData.Stats.LaunchCount}次, 使用{bestData.Stats.TotalUsageMinutes}分钟"); + } + else + { + results.AppendLine(" 未找到可信数据"); + } + + // 3. 测试部分恢复 + results.AppendLine("\n3. 部分数据恢复测试:"); + var partialRecovery = AttemptPartialDataRecovery(allSources); + if (partialRecovery != null) + { + results.AppendLine($" 恢复成功: 启动{partialRecovery.LaunchCount}次, 使用{partialRecovery.TotalUsageMinutes}分钟"); + results.AppendLine($" 完整性验证: {partialRecovery.VerifyDataIntegrity()}"); + } + else + { + results.AppendLine(" 部分恢复失败"); + } + + // 4. 测试实际加载 + results.AppendLine("\n4. 实际加载测试:"); + var loadedStats = LoadUsageStats(); + if (loadedStats != null) + { + results.AppendLine($" 加载成功: 启动{loadedStats.LaunchCount}次, 使用{loadedStats.TotalUsageMinutes}分钟"); + results.AppendLine($" 完整性验证: {loadedStats.VerifyDataIntegrity()}"); + results.AppendLine($" 使用频率: {loadedStats.UsageFrequency}"); + results.AppendLine($" 更新优先级: {loadedStats.UpdatePriority}"); + } + else + { + results.AppendLine(" 加载失败"); + } + + return results.ToString(); + } + } + catch (Exception ex) + { + results.AppendLine($"\n测试失败: {ex.Message}"); + LogHelper.WriteLogToFile($"DeviceIdentifier | 反篡改恢复测试失败: {ex.Message}", LogHelper.LogType.Error); + return results.ToString(); + } + } + + /// + /// 模拟数据篡改并测试恢复 + /// + public static string SimulateDataTamperingAndRecover() + { + var results = new StringBuilder(); + results.AppendLine("数据篡改模拟和恢复测试:"); + + try + { + lock (fileLock) + { + // 1. 保存当前正常数据 + results.AppendLine("\n1. 保存当前正常数据作为备份..."); + var originalStats = LoadUsageStats(); + if (originalStats == null) + { + results.AppendLine(" 无法获取原始数据,创建测试数据"); + originalStats = new UsageStats + { + DeviceId = DeviceId, + LaunchCount = 50, + TotalUsageMinutes = 1200, + LastLaunchTime = DateTime.Now.AddDays(-1), + UpdatePriority = UpdatePriority.Medium, + UsageFrequency = UsageFrequency.Medium + }; + originalStats.UpdateDataHash(); + } + + SaveUsageStatsToAllLocations(originalStats); + results.AppendLine($" 原始数据: 启动{originalStats.LaunchCount}次, 使用{originalStats.TotalUsageMinutes}分钟"); + + // 2. 模拟篡改主文件 + results.AppendLine("\n2. 模拟篡改主文件..."); + try + { + var tamperedStats = new UsageStats + { + DeviceId = DeviceId, + LaunchCount = 1, // 篡改为很小的值 + TotalUsageMinutes = 5, + LastLaunchTime = DateTime.Now, + UpdatePriority = UpdatePriority.Low, + UsageFrequency = UsageFrequency.Low, + DataHash = "TAMPERED_HASH", // 错误的哈希值 + LastModified = DateTime.Now + }; + + var tamperedJson = JsonConvert.SerializeObject(tamperedStats, Formatting.Indented); + File.WriteAllText(UsageStatsFilePath, tamperedJson); + results.AppendLine(" 主文件已被篡改"); + } + catch (Exception ex) + { + results.AppendLine($" 篡改主文件失败: {ex.Message}"); + } + + // 3. 测试恢复机制 + results.AppendLine("\n3. 测试智能恢复机制..."); + var recoveredStats = LoadUsageStats(); + if (recoveredStats != null) + { + results.AppendLine($" 恢复成功: 启动{recoveredStats.LaunchCount}次, 使用{recoveredStats.TotalUsageMinutes}分钟"); + results.AppendLine($" 完整性验证: {recoveredStats.VerifyDataIntegrity()}"); + + // 检查是否恢复了正确的数据 + if (Math.Abs(recoveredStats.LaunchCount - originalStats.LaunchCount) <= 5 && + Math.Abs(recoveredStats.TotalUsageMinutes - originalStats.TotalUsageMinutes) <= 100) + { + results.AppendLine(" ✓ 数据恢复正确,反篡改机制工作正常"); + } + else + { + results.AppendLine(" ✗ 数据恢复不完全正确,但避免了使用篡改数据"); + } + } + else + { + results.AppendLine(" ✗ 恢复失败"); + } + + // 4. 恢复正常状态 + results.AppendLine("\n4. 恢复正常状态..."); + SaveUsageStatsToAllLocations(originalStats); + results.AppendLine(" 系统已恢复到正常状态"); + + return results.ToString(); + } + } + catch (Exception ex) + { + results.AppendLine($"\n模拟测试失败: {ex.Message}"); + LogHelper.WriteLogToFile($"DeviceIdentifier | 数据篡改模拟测试失败: {ex.Message}", LogHelper.LogType.Error); + return results.ToString(); + } + } + } +} \ No newline at end of file diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 81ddbb30..eb456c64 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -2594,6 +2594,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +