Files
community/Ink Canvas/Helpers/DeviceIdentifier.cs
T
2025-07-28 18:11:20 +08:00

2749 lines
121 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 设备标识符和使用频率监控类
/// </summary>
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;
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);
}
}
/// <summary>
/// 获取或创建设备ID
/// </summary>
/// <returns>25字符的唯一设备标识符</returns>
public static string GetDeviceId()
{
return DeviceId;
}
/// <summary>
/// 获取或创建设备ID(内部方法)- 支持多重备份恢复
/// </summary>
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();
}
}
}
/// <summary>
/// 生成25字符的唯一设备ID
/// </summary>
private static string GenerateDeviceId()
{
try
{
// 收集硬件信息
var hardwareInfo = new StringBuilder();
// 使用反射获取硬件信息,避免直接引用System.Management
try
{
// 尝试加载System.Management程序集
var assembly = 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);
}
// 生成哈希
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();
}
}
/// <summary>
/// 生成备用设备ID(基于时间戳)
/// </summary>
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";
}
}
/// <summary>
/// 验证设备ID格式
/// </summary>
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;
}
}
/// <summary>
/// 从文件加载设备ID
/// </summary>
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;
}
/// <summary>
/// 从注册表加载设备ID
/// </summary>
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;
}
/// <summary>
/// 保存设备ID到所有位置
/// </summary>
private static void SaveDeviceIdToAllLocations(string deviceId)
{
// 保存到主文件
SaveDeviceIdToFile(DeviceIdFilePath, deviceId);
// 保存到备份文件
SaveDeviceIdToFile(BackupDeviceIdPath, deviceId);
// 保存到注册表
SaveDeviceIdToRegistry(deviceId);
}
/// <summary>
/// 保存设备ID到文件
/// </summary>
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);
}
}
/// <summary>
/// 保存设备ID到注册表
/// </summary>
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);
}
}
/// <summary>
/// 使用频率统计数据结构(优化至秒级精度)
/// </summary>
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("totalUsageSeconds")]
public long TotalUsageSeconds { get; set; }
[JsonProperty("averageSessionSeconds")]
public double AverageSessionSeconds { get; set; }
// 保留旧字段以保持向后兼容性(已弃用)
[JsonProperty("totalUsageMinutes")]
[Obsolete("已弃用,请使用 TotalUsageSeconds")]
public long TotalUsageMinutes { get; set; }
[JsonProperty("averageSessionMinutes")]
[Obsolete("已弃用,请使用 AverageSessionSeconds")]
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; }
// 每周统计数据(秒级精度)
[JsonProperty("weeklyLaunchCount")]
public int WeeklyLaunchCount { get; set; }
[JsonProperty("weeklyUsageSeconds")]
public long WeeklyUsageSeconds { get; set; }
[JsonProperty("weekStartDate")]
public DateTime WeekStartDate { get; set; }
[JsonProperty("lastWeekLaunchCount")]
public int LastWeekLaunchCount { get; set; }
[JsonProperty("lastWeekUsageSeconds")]
public long LastWeekUsageSeconds { get; set; }
// 保留旧字段以保持向后兼容性(已弃用)
[JsonProperty("weeklyUsageMinutes")]
[Obsolete("已弃用,请使用 WeeklyUsageSeconds")]
public long WeeklyUsageMinutes { get; set; }
[JsonProperty("lastWeekUsageMinutes")]
[Obsolete("已弃用,请使用 LastWeekUsageSeconds")]
public long LastWeekUsageMinutes { get; set; }
/// <summary>
/// 数据迁移:从分钟精度迁移到秒级精度
/// </summary>
public void MigrateToSecondsPrecision()
{
try
{
// 如果新字段为空但旧字段有数据,进行迁移
if (TotalUsageSeconds == 0 && TotalUsageMinutes > 0)
{
TotalUsageSeconds = TotalUsageMinutes * 60;
LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移总使用时长: {TotalUsageMinutes}分钟 -> {TotalUsageSeconds}秒");
}
if (AverageSessionSeconds == 0 && AverageSessionMinutes > 0)
{
AverageSessionSeconds = AverageSessionMinutes * 60;
LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移平均会话时长: {AverageSessionMinutes}分钟 -> {AverageSessionSeconds}秒");
}
if (WeeklyUsageSeconds == 0 && WeeklyUsageMinutes > 0)
{
WeeklyUsageSeconds = WeeklyUsageMinutes * 60;
LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移每周使用时长: {WeeklyUsageMinutes}分钟 -> {WeeklyUsageSeconds}秒");
}
if (LastWeekUsageSeconds == 0 && LastWeekUsageMinutes > 0)
{
LastWeekUsageSeconds = LastWeekUsageMinutes * 60;
LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移上周使用时长: {LastWeekUsageMinutes}分钟 -> {LastWeekUsageSeconds}秒");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 数据迁移失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 检查并重置每周统计数据(秒级精度)
/// </summary>
public void CheckAndResetWeeklyStats()
{
var now = DateTime.Now;
var currentWeekStart = GetWeekStartDate(now);
// 如果是新的一周,重置统计
if (WeekStartDate == DateTime.MinValue || currentWeekStart > WeekStartDate)
{
// 保存上周数据
LastWeekLaunchCount = WeeklyLaunchCount;
LastWeekUsageSeconds = WeeklyUsageSeconds;
// 同时更新旧字段以保持兼容性
LastWeekUsageMinutes = LastWeekUsageSeconds / 60;
// 重置本周数据
WeeklyLaunchCount = 0;
WeeklyUsageSeconds = 0;
WeeklyUsageMinutes = 0;
WeekStartDate = currentWeekStart;
LogHelper.WriteLogToFile($"DeviceIdentifier | 每周统计重置 - 上周启动: {LastWeekLaunchCount}次, 上周使用: {FormatDuration(LastWeekUsageSeconds)}");
}
}
/// <summary>
/// 获取指定日期所在周的开始日期(周一)
/// </summary>
public DateTime GetWeekStartDate(DateTime date)
{
var dayOfWeek = (int)date.DayOfWeek;
var daysToSubtract = dayOfWeek == 0 ? 6 : dayOfWeek - 1; // 周日=0,需要减6天到周一
return date.Date.AddDays(-daysToSubtract);
}
/// <summary>
/// 记录本周的启动
/// </summary>
public void RecordWeeklyLaunch()
{
CheckAndResetWeeklyStats();
WeeklyLaunchCount++;
}
/// <summary>
/// 记录本周的使用时长(秒级精度)
/// </summary>
public void RecordWeeklyUsage(long seconds)
{
CheckAndResetWeeklyStats();
WeeklyUsageSeconds += seconds;
// 同时更新旧字段以保持兼容性
WeeklyUsageMinutes = WeeklyUsageSeconds / 60;
}
/// <summary>
/// 计算数据哈希值用于完整性验证(秒级精度)
/// </summary>
public void UpdateDataHash()
{
// 使用秒级精度数据计算哈希
var dataString = $"{DeviceId}|{LaunchCount}|{TotalUsageSeconds}|{LastLaunchTime:yyyyMMddHHmmss}|{WeeklyLaunchCount}|{WeeklyUsageSeconds}|{DataIntegrityKey}";
using (var sha256 = SHA256.Create())
{
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(dataString));
DataHash = Convert.ToBase64String(hashBytes);
}
LastModified = DateTime.Now;
}
/// <summary>
/// 验证数据完整性(秒级精度)
/// </summary>
public bool VerifyDataIntegrity()
{
try
{
// 首先尝试使用秒级精度验证
var dataString = $"{DeviceId}|{LaunchCount}|{TotalUsageSeconds}|{LastLaunchTime:yyyyMMddHHmmss}|{WeeklyLaunchCount}|{WeeklyUsageSeconds}|{DataIntegrityKey}";
using (var sha256 = SHA256.Create())
{
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(dataString));
var expectedHash = Convert.ToBase64String(hashBytes);
if (DataHash == expectedHash)
{
return true;
}
}
// 如果秒级精度验证失败,尝试使用旧的分钟精度验证(向后兼容)
var oldDataString = $"{DeviceId}|{LaunchCount}|{TotalUsageMinutes}|{LastLaunchTime:yyyyMMddHHmmss}|{WeeklyLaunchCount}|{WeeklyUsageMinutes}|{DataIntegrityKey}";
using (var sha256 = SHA256.Create())
{
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(oldDataString));
var expectedHash = Convert.ToBase64String(hashBytes);
return DataHash == expectedHash;
}
}
catch
{
return false;
}
}
}
/// <summary>
/// 格式化时长显示(秒级精度)
/// </summary>
/// <param name="totalSeconds">总秒数</param>
/// <returns>格式化的时长字符串</returns>
public static string FormatDuration(long totalSeconds)
{
if (totalSeconds < 60)
{
return $"{totalSeconds}秒";
}
if (totalSeconds < 3600)
{
var minutes = totalSeconds / 60;
var seconds = totalSeconds % 60;
return seconds > 0 ? $"{minutes}分{seconds}秒" : $"{minutes}分钟";
}
else
{
var hours = totalSeconds / 3600;
var minutes = (totalSeconds % 3600) / 60;
var seconds = totalSeconds % 60;
var result = $"{hours}小时";
if (minutes > 0) result += $"{minutes}分";
if (seconds > 0) result += $"{seconds}秒";
return result;
}
}
/// <summary>
/// 更新推送优先级枚举
/// </summary>
public enum UpdatePriority
{
High = 1, // 高优先级:立即推送更新
Medium = 2, // 中优先级:延迟1-3天推送
Low = 3 // 低优先级:延迟3-14天推送
}
/// <summary>
/// 用户使用频率分类枚举
/// </summary>
public enum UsageFrequency
{
High = 1, // 高频用户:综合评分≥80分(活跃度高、使用时长长、启动频繁)
Medium = 2, // 中频用户:综合评分40-79分(中等活跃度和使用强度)
Low = 3 // 低频用户:综合评分<40分(活跃度低、使用时长短、启动较少)
}
/// <summary>
/// 记录应用启动
/// </summary>
public static void RecordAppLaunch()
{
try
{
lock (fileLock)
{
var stats = LoadUsageStats();
stats.LastLaunchTime = DateTime.Now;
stats.LaunchCount++;
stats.DeviceId = DeviceId;
// 记录每周启动次数
stats.RecordWeeklyLaunch();
// 计算使用频率
CalculateUsageFrequency(stats);
// 更新数据完整性哈希
stats.UpdateDataHash();
SaveUsageStats(stats);
LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用启动 - 设备ID: {DeviceId}, 总启动: {stats.LaunchCount}次, 本周启动: {stats.WeeklyLaunchCount}次");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用启动失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 记录应用退出(计算使用时长 - 秒级精度)
/// </summary>
public static void RecordAppExit()
{
try
{
lock (fileLock)
{
var stats = LoadUsageStats();
// 执行数据迁移(如果需要)
stats.MigrateToSecondsPrecision();
// 计算本次会话时长(秒级精度)
long sessionSeconds = 0;
if (stats.LastLaunchTime != DateTime.MinValue)
{
var sessionDuration = DateTime.Now - stats.LastLaunchTime;
sessionSeconds = (long)sessionDuration.TotalSeconds;
// 更新秒级精度数据
stats.TotalUsageSeconds += sessionSeconds;
// 同时更新旧字段以保持兼容性
stats.TotalUsageMinutes = stats.TotalUsageSeconds / 60;
// 记录每周使用时长(秒级精度)
stats.RecordWeeklyUsage(sessionSeconds);
// 更新平均会话时长(秒级精度)
if (stats.LaunchCount > 0)
{
stats.AverageSessionSeconds = (double)stats.TotalUsageSeconds / stats.LaunchCount;
// 同时更新旧字段以保持兼容性
stats.AverageSessionMinutes = stats.AverageSessionSeconds / 60;
}
}
// 重新计算使用频率
CalculateUsageFrequency(stats);
// 更新数据完整性哈希
stats.UpdateDataHash();
SaveUsageStats(stats);
LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用退出 - 本次会话: {FormatDuration(sessionSeconds)}, " +
$"总时长: {FormatDuration(stats.TotalUsageSeconds)}, " +
$"本周时长: {FormatDuration(stats.WeeklyUsageSeconds)}");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用退出失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 计算使用频率和更新优先级(基于真实的每周统计数据)
/// 通过多维度评分系统确定用户类型:高频(≥80分)、中频(40-79分)、低频(<40分)
/// </summary>
private static void CalculateUsageFrequency(UsageStats stats)
{
try
{
// 确保每周统计数据是最新的
stats.CheckAndResetWeeklyStats();
// 计算最近活跃度
var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays;
// 使用真实的每周数据(秒级精度)
var currentWeekLaunches = stats.WeeklyLaunchCount;
var currentWeekSeconds = stats.WeeklyUsageSeconds;
// 如果秒级数据为空但分钟数据存在,进行转换
if (currentWeekSeconds == 0 && stats.WeeklyUsageMinutes > 0)
{
currentWeekSeconds = stats.WeeklyUsageMinutes * 60;
}
// 如果本周数据不足,参考上周数据
var weeklyLaunches = currentWeekLaunches > 0 ? currentWeekLaunches : stats.LastWeekLaunchCount;
var weeklySeconds = currentWeekSeconds > 0 ? currentWeekSeconds : stats.LastWeekUsageSeconds;
// 如果秒级数据仍为空,使用分钟数据转换
if (weeklySeconds == 0 && stats.LastWeekUsageMinutes > 0)
{
weeklySeconds = stats.LastWeekUsageMinutes * 60;
}
// 综合评分系统(0-100分)
var frequencyScore = CalculateFrequencyScoreWithWeeklyData(stats, daysSinceLastUse, weeklyLaunches, weeklySeconds);
// 根据综合评分确定频率分类和更新优先级
if (frequencyScore >= 80)
{
stats.UsageFrequency = UsageFrequency.High; // 高频用户:立即推送更新
stats.UpdatePriority = UpdatePriority.High;
}
else if (frequencyScore >= 40)
{
stats.UsageFrequency = UsageFrequency.Medium; // 中频用户:延迟1-3天推送
stats.UpdatePriority = UpdatePriority.Medium;
}
else
{
stats.UsageFrequency = UsageFrequency.Low; // 低频用户:延迟3-14天推送
stats.UpdatePriority = UpdatePriority.Low;
}
LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率计算 - 评分: {frequencyScore}, 频率: {stats.UsageFrequency}, " +
$"优先级: {stats.UpdatePriority}, 本周启动: {currentWeekLaunches}次, 本周时长: {FormatDuration(currentWeekSeconds)}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 计算使用频率失败: {ex.Message}", LogHelper.LogType.Error);
// 默认设置为中等频率和优先级
stats.UsageFrequency = UsageFrequency.Medium;
stats.UpdatePriority = UpdatePriority.Medium;
}
}
/// <summary>
/// 基于每周真实数据计算综合频率评分(0-100分,秒级精度)
/// 评分标准:≥80分=高频用户,40-79分=中频用户,<40分=低频用户
/// </summary>
/// <param name="stats">使用统计数据</param>
/// <param name="daysSinceLastUse">距离最后使用的天数</param>
/// <param name="weeklyLaunches">每周启动次数</param>
/// <param name="weeklySeconds">每周使用时长(秒)</param>
/// <returns>综合评分(0-100分)</returns>
private static int CalculateFrequencyScoreWithWeeklyData(UsageStats stats, double daysSinceLastUse,
long weeklyLaunches, long weeklySeconds)
{
var score = 0;
// 最近活跃度评分(40分)- 反映用户当前的活跃程度
if (daysSinceLastUse <= 1) score += 40; // 1天内使用:非常活跃
else if (daysSinceLastUse <= 3) score += 35; // 3天内使用:很活跃
else if (daysSinceLastUse <= 7) score += 25; // 1周内使用:较活跃
else if (daysSinceLastUse <= 14) score += 15; // 2周内使用:一般活跃
else if (daysSinceLastUse <= 30) score += 5; // 1月内使用:不太活跃
// 每周使用频率评分(30分)- 基于真实的每周启动次数
if (weeklyLaunches >= 10) score += 30; // 10次以上:高频使用
else if (weeklyLaunches >= 5) score += 20; // 5-9次:中高频使用
else if (weeklyLaunches >= 3) score += 15; // 3-4次:中频使用
else if (weeklyLaunches >= 1) score += 10; // 1-2次:低频使用
// 每周使用时长评分(20分)- 基于真实的每周使用时长(秒级精度)
if (weeklySeconds >= 36000) score += 20; // 10小时以上:重度使用
else if (weeklySeconds >= 18000) score += 15; // 5-10小时:中重度使用
else if (weeklySeconds >= 7200) score += 10; // 2-5小时:中度使用
else if (weeklySeconds >= 3600) score += 5; // 1-2小时:轻度使用
// 历史使用深度评分(10分)- 反映用户的长期使用习惯(秒级精度)
var totalSeconds = stats.TotalUsageSeconds > 0 ? stats.TotalUsageSeconds : stats.TotalUsageMinutes * 60;
if (totalSeconds >= 180000) score += 10; // 50小时以上:资深用户
else if (totalSeconds >= 72000) score += 7; // 20-50小时:中等用户
else if (totalSeconds >= 18000) score += 4; // 5-20小时:新手用户
return Math.Min(100, score);
}
/// <summary>
/// 获取当前更新优先级
/// </summary>
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; // 默认中等优先级
}
}
/// <summary>
/// 获取使用频率
/// </summary>
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; // 默认中等频率
}
}
/// <summary>
/// 获取使用统计信息(秒级精度)
/// </summary>
public static (int launchCount, long totalSeconds, double avgSessionSeconds, UpdatePriority priority) GetUsageStats()
{
try
{
var stats = LoadUsageStats();
return (stats.LaunchCount, stats.TotalUsageSeconds, stats.AverageSessionSeconds, stats.UpdatePriority);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 获取使用统计失败: {ex.Message}", LogHelper.LogType.Error);
return (0, 0, 0, UpdatePriority.Medium);
}
}
/// <summary>
/// 获取使用统计信息(兼容性方法 - 分钟精度)
/// </summary>
[Obsolete("请使用 GetUsageStats() 获取秒级精度数据")]
public static (int launchCount, long totalMinutes, double avgSessionMinutes, UpdatePriority priority) GetUsageStatsInMinutes()
{
try
{
var stats = LoadUsageStats();
var totalMinutes = stats.TotalUsageSeconds / 60;
var avgMinutes = stats.AverageSessionSeconds / 60;
return (stats.LaunchCount, totalMinutes, avgMinutes, stats.UpdatePriority);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 获取使用统计失败: {ex.Message}", LogHelper.LogType.Error);
return (0, 0, 0, UpdatePriority.Medium);
}
}
/// <summary>
/// 加载使用统计 - 支持多重备份恢复和智能反篡改
/// </summary>
private static UsageStats LoadUsageStats()
{
try
{
// 智能恢复:收集所有可用的数据源,选择最可信的
var allDataSources = CollectAllUsageDataSources();
// 如果找到有效数据,返回最可信的
if (allDataSources.Count > 0)
{
var bestData = SelectMostTrustedData(allDataSources);
if (bestData != null)
{
// LogHelper.WriteLogToFile($"DeviceIdentifier | 使用最可信数据源恢复使用统计: {bestData.Source}");
// 执行数据迁移(如果需要)
bestData.Stats.MigrateToSecondsPrecision();
// 确保备份同步
SaveUsageStatsToAllLocations(bestData.Stats);
return bestData.Stats;
}
}
// LogHelper.WriteLogToFile("DeviceIdentifier | 所有数据源都不可用,检查是否有部分可恢复数据", LogHelper.LogType.Warning);
// 如果没有完全可信的数据,尝试从部分损坏的数据中恢复
var partiallyRecoveredData = AttemptPartialDataRecovery(allDataSources);
if (partiallyRecoveredData != null)
{
// LogHelper.WriteLogToFile("DeviceIdentifier | 从部分损坏数据中恢复使用统计");
// 执行数据迁移(如果需要)
partiallyRecoveredData.MigrateToSecondsPrecision();
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,
TotalUsageSeconds = 0,
AverageSessionSeconds = 0,
TotalUsageMinutes = 0, // 保持兼容性
AverageSessionMinutes = 0, // 保持兼容性
LastUpdateCheck = DateTime.MinValue,
UpdatePriority = UpdatePriority.Medium,
UsageFrequency = UsageFrequency.Medium
};
// 更新数据完整性哈希
newStats.UpdateDataHash();
// 保存新统计到所有位置
SaveUsageStatsToAllLocations(newStats);
return newStats;
}
/// <summary>
/// 保存使用统计 - 多重备份
/// </summary>
private static void SaveUsageStats(UsageStats stats)
{
SaveUsageStatsToAllLocations(stats);
}
/// <summary>
/// 数据源信息结构
/// </summary>
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; }
}
/// <summary>
/// 从文件加载使用统计(带完整性验证,但不丢弃篡改数据)
/// </summary>
private static UsageStats LoadUsageStatsFromFile(string filePath)
{
try
{
if (File.Exists(filePath))
{
string json = File.ReadAllText(filePath);
var stats = JsonConvert.DeserializeObject<UsageStats>(json);
if (stats != null && !string.IsNullOrEmpty(stats.DeviceId))
{
// 验证数据完整性
if (!string.IsNullOrEmpty(stats.DataHash))
{
if (stats.VerifyDataIntegrity())
{
// LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性验证通过: {filePath}");
return stats;
}
// LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性验证失败,可能被篡改: {filePath}", LogHelper.LogType.Warning);
return null; // 数据被篡改,不使用
}
// 旧版本数据,没有哈希值,更新哈希后返回
// LogHelper.WriteLogToFile($"DeviceIdentifier | 检测到旧版本数据,正在更新完整性哈希: {filePath}");
stats.UpdateDataHash();
return stats;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 从文件加载使用统计失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error);
}
return null;
}
/// <summary>
/// 从文件加载使用统计(包括被篡改的数据,用于恢复分析)
/// </summary>
private static DataSourceInfo LoadUsageStatsFromFileWithInfo(string filePath, string sourceName)
{
try
{
if (File.Exists(filePath))
{
string json = File.ReadAllText(filePath);
var stats = JsonConvert.DeserializeObject<UsageStats>(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;
}
/// <summary>
/// 收集所有可用的使用统计数据源
/// </summary>
private static List<DataSourceInfo> CollectAllUsageDataSources()
{
var dataSources = new List<DataSourceInfo>();
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;
}
}
/// <summary>
/// 选择最可信的数据源
/// </summary>
private static DataSourceInfo SelectMostTrustedData(List<DataSourceInfo> 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;
}
}
/// <summary>
/// 尝试从部分损坏的数据中恢复
/// </summary>
private static UsageStats AttemptPartialDataRecovery(List<DataSourceInfo> 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 usageSeconds = dataSources.Where(d => d.Stats.TotalUsageSeconds > 0).Select(d => d.Stats.TotalUsageSeconds).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 (usageSeconds.Count > 0)
{
recoveredStats.TotalUsageSeconds = (long)usageSeconds.Average(); // 使用平均值
recoveredStats.TotalUsageMinutes = recoveredStats.TotalUsageSeconds / 60; // 兼容性
}
else if (usageMinutes.Count > 0)
{
recoveredStats.TotalUsageMinutes = (long)usageMinutes.Average(); // 使用平均值
recoveredStats.TotalUsageSeconds = recoveredStats.TotalUsageMinutes * 60; // 转换为秒
}
// 重新计算平均会话时长(秒级精度)
if (recoveredStats.LaunchCount > 0)
{
recoveredStats.AverageSessionSeconds = (double)recoveredStats.TotalUsageSeconds / recoveredStats.LaunchCount;
recoveredStats.AverageSessionMinutes = recoveredStats.AverageSessionSeconds / 60; // 兼容性
}
// 重新计算使用频率
CalculateUsageFrequency(recoveredStats);
// 更新数据完整性哈希
recoveredStats.UpdateDataHash();
// LogHelper.WriteLogToFile($"DeviceIdentifier | 部分数据恢复完成 - 启动次数: {recoveredStats.LaunchCount}, 使用时长: {FormatDuration(recoveredStats.TotalUsageSeconds)}");
return recoveredStats;
}
catch (Exception ex)
{
// LogHelper.WriteLogToFile($"DeviceIdentifier | 部分数据恢复失败: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
/// <summary>
/// 从注册表加载使用统计(带数据源信息)
/// </summary>
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 totalSeconds = key.GetValue("TotalUsageSeconds");
var avgSessionSeconds = key.GetValue("AverageSessionSeconds");
// 兼容性:分钟精度数据
var totalMinutes = key.GetValue("TotalUsageMinutes");
var avgSessionMinutes = key.GetValue("AverageSessionMinutes");
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;
// 每周统计数据(秒级精度)
var weeklyLaunchCount = key.GetValue("WeeklyLaunchCount");
var weeklyUsageSeconds = key.GetValue("WeeklyUsageSeconds");
var lastWeekUsageSeconds = key.GetValue("LastWeekUsageSeconds");
// 兼容性:分钟精度数据
var weeklyUsageMinutes = key.GetValue("WeeklyUsageMinutes");
var lastWeekUsageMinutes = key.GetValue("LastWeekUsageMinutes");
var weekStartDate = key.GetValue("WeekStartDate") as string;
var lastWeekLaunchCount = key.GetValue("LastWeekLaunchCount");
if (!string.IsNullOrEmpty(deviceId) && launchCount != null)
{
var stats = new UsageStats
{
DeviceId = deviceId,
LaunchCount = Convert.ToInt32(launchCount),
// 秒级精度数据
TotalUsageSeconds = totalSeconds != null ? Convert.ToInt64(totalSeconds) : 0,
AverageSessionSeconds = avgSessionSeconds != null ? Convert.ToDouble(avgSessionSeconds) : 0,
// 兼容性:分钟精度数据
TotalUsageMinutes = totalMinutes != null ? Convert.ToInt64(totalMinutes) : 0,
AverageSessionMinutes = avgSessionMinutes != null ? Convert.ToDouble(avgSessionMinutes) : 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,
LastUpdateCheck = DateTime.MinValue,
// 每周统计数据(秒级精度)
WeeklyLaunchCount = weeklyLaunchCount != null ? Convert.ToInt32(weeklyLaunchCount) : 0,
WeeklyUsageSeconds = weeklyUsageSeconds != null ? Convert.ToInt64(weeklyUsageSeconds) : 0,
LastWeekUsageSeconds = lastWeekUsageSeconds != null ? Convert.ToInt64(lastWeekUsageSeconds) : 0,
// 兼容性:分钟精度数据
WeeklyUsageMinutes = weeklyUsageMinutes != null ? Convert.ToInt64(weeklyUsageMinutes) : 0,
LastWeekUsageMinutes = lastWeekUsageMinutes != null ? Convert.ToInt64(lastWeekUsageMinutes) : 0,
WeekStartDate = DateTime.TryParse(weekStartDate, out var wsd) ? wsd : DateTime.MinValue,
LastWeekLaunchCount = lastWeekLaunchCount != null ? Convert.ToInt32(lastWeekLaunchCount) : 0
};
// 执行数据迁移(如果需要)
stats.MigrateToSecondsPrecision();
// 重新计算平均会话时长
if (stats.LaunchCount > 0 && stats.AverageSessionSeconds == 0)
{
stats.AverageSessionSeconds = (double)stats.TotalUsageSeconds / stats.LaunchCount;
stats.AverageSessionMinutes = stats.AverageSessionSeconds / 60;
}
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;
}
/// <summary>
/// 从备用注册表位置加载使用统计(带数据源信息)
/// </summary>
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 totalSeconds = key.GetValue("TUS");
var avgSessionSeconds = key.GetValue("ASS");
// 兼容性:分钟精度数据
var totalMinutes = key.GetValue("TUM");
var avgSessionMinutes = key.GetValue("ASM");
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");
// 每周统计数据(秒级精度)
var weeklyLaunchCount = key.GetValue("WLC");
var weeklyUsageSeconds = key.GetValue("WUS");
var lastWeekUsageSeconds = key.GetValue("LWUS");
// 兼容性:分钟精度数据
var weeklyUsageMinutes = key.GetValue("WUM");
var lastWeekUsageMinutes = key.GetValue("LWUM");
var weekStartDateBinary = key.GetValue("WSD");
var lastWeekLaunchCount = key.GetValue("LWLC");
if (launchCount != null && (totalSeconds != null || totalMinutes != null))
{
var stats = new UsageStats
{
DeviceId = DeviceId,
LaunchCount = Convert.ToInt32(launchCount),
// 秒级精度数据
TotalUsageSeconds = totalSeconds != null ? Convert.ToInt64(totalSeconds) : 0,
AverageSessionSeconds = avgSessionSeconds != null ? Convert.ToDouble(avgSessionSeconds) : 0,
// 兼容性:分钟精度数据
TotalUsageMinutes = totalMinutes != null ? Convert.ToInt64(totalMinutes) : 0,
AverageSessionMinutes = avgSessionMinutes != null ? Convert.ToDouble(avgSessionMinutes) : 0,
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,
LastUpdateCheck = DateTime.MinValue,
// 每周统计数据(秒级精度)
WeeklyLaunchCount = weeklyLaunchCount != null ? Convert.ToInt32(weeklyLaunchCount) : 0,
WeeklyUsageSeconds = weeklyUsageSeconds != null ? Convert.ToInt64(weeklyUsageSeconds) : 0,
LastWeekUsageSeconds = lastWeekUsageSeconds != null ? Convert.ToInt64(lastWeekUsageSeconds) : 0,
// 兼容性:分钟精度数据
WeeklyUsageMinutes = weeklyUsageMinutes != null ? Convert.ToInt64(weeklyUsageMinutes) : 0,
LastWeekUsageMinutes = lastWeekUsageMinutes != null ? Convert.ToInt64(lastWeekUsageMinutes) : 0,
WeekStartDate = weekStartDateBinary != null ? DateTime.FromBinary(Convert.ToInt64(weekStartDateBinary)) : DateTime.MinValue,
LastWeekLaunchCount = lastWeekLaunchCount != null ? Convert.ToInt32(lastWeekLaunchCount) : 0
};
// 执行数据迁移(如果需要)
stats.MigrateToSecondsPrecision();
// 重新计算平均会话时长
if (stats.LaunchCount > 0 && stats.AverageSessionSeconds == 0)
{
stats.AverageSessionSeconds = (double)stats.TotalUsageSeconds / stats.LaunchCount;
stats.AverageSessionMinutes = stats.AverageSessionSeconds / 60;
}
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;
}
/// <summary>
/// 从注册表加载使用统计(保持向后兼容)
/// </summary>
private static UsageStats LoadUsageStatsFromRegistry()
{
var dataSource = LoadUsageStatsFromRegistryWithInfo(@"Software\ICC\DeviceInfo", "主注册表");
return dataSource?.Stats;
}
/// <summary>
/// 从多个注册表位置加载使用统计(强化恢复)
/// </summary>
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;
}
// LogHelper.WriteLogToFile($"DeviceIdentifier | 注册表位置数据完整性验证失败: {path}", LogHelper.LogType.Warning);
}
else
{
// 没有哈希值的旧数据,更新哈希后返回
stats.UpdateDataHash();
// LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表位置恢复旧版本数据: {path}");
return stats;
}
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表位置加载失败 ({path}): {ex.Message}", LogHelper.LogType.Error);
}
}
return null;
}
/// <summary>
/// 保存使用统计到所有位置(强化版本 - 多重隐藏备份)
/// </summary>
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);
}
/// <summary>
/// 保存使用统计到文件
/// </summary>
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);
}
}
/// <summary>
/// 保存使用统计到注册表
/// </summary>
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("TotalUsageSeconds", stats.TotalUsageSeconds);
key.SetValue("AverageSessionSeconds", stats.AverageSessionSeconds);
// 兼容性:分钟精度数据
key.SetValue("TotalUsageMinutes", stats.TotalUsageMinutes);
key.SetValue("AverageSessionMinutes", stats.AverageSessionMinutes);
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"));
// 每周统计数据(秒级精度)
key.SetValue("WeeklyLaunchCount", stats.WeeklyLaunchCount);
key.SetValue("WeeklyUsageSeconds", stats.WeeklyUsageSeconds);
key.SetValue("LastWeekUsageSeconds", stats.LastWeekUsageSeconds);
// 兼容性:分钟精度数据
key.SetValue("WeeklyUsageMinutes", stats.WeeklyUsageMinutes);
key.SetValue("LastWeekUsageMinutes", stats.LastWeekUsageMinutes);
key.SetValue("WeekStartDate", stats.WeekStartDate.ToString("yyyy-MM-dd"));
key.SetValue("LastWeekLaunchCount", stats.LastWeekLaunchCount);
// LogHelper.WriteLogToFile($"DeviceIdentifier | 使用统计已保存到主注册表 - 总启动: {stats.LaunchCount}次, 本周启动: {stats.WeeklyLaunchCount}次, " +
// $"总时长: {FormatDuration(stats.TotalUsageSeconds)}, 本周时长: {FormatDuration(stats.WeeklyUsageSeconds)}");
}
else
{
// LogHelper.WriteLogToFile("DeviceIdentifier | 创建主注册表键失败", LogHelper.LogType.Error);
}
}
}
catch (Exception ex)
{
// LogHelper.WriteLogToFile($"DeviceIdentifier | 保存使用统计到主注册表失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存使用统计到多个注册表位置(强化保护)
/// </summary>
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("TUS", stats.TotalUsageSeconds); // TotalUsageSeconds
key.SetValue("ASS", stats.AverageSessionSeconds); // AverageSessionSeconds
// 兼容性:分钟精度数据
key.SetValue("TUM", stats.TotalUsageMinutes); // TotalUsageMinutes
key.SetValue("ASM", stats.AverageSessionMinutes); // AverageSessionMinutes
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
// 每周统计数据(秒级精度)
key.SetValue("WLC", stats.WeeklyLaunchCount); // WeeklyLaunchCount
key.SetValue("WUS", stats.WeeklyUsageSeconds); // WeeklyUsageSeconds
key.SetValue("LWUS", stats.LastWeekUsageSeconds); // LastWeekUsageSeconds
// 兼容性:分钟精度数据
key.SetValue("WUM", stats.WeeklyUsageMinutes); // WeeklyUsageMinutes
key.SetValue("LWUM", stats.LastWeekUsageMinutes); // LastWeekUsageMinutes
key.SetValue("WSD", stats.WeekStartDate.ToBinary()); // WeekStartDate
key.SetValue("LWLC", stats.LastWeekLaunchCount); // LastWeekLaunchCount
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} 成功");
}
/// <summary>
/// 记录更新检查时间(同时执行数据保护检查)
/// </summary>
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);
}
}
/// <summary>
/// 执行使用频率数据保护检查和自动修复
/// </summary>
public static bool PerformUsageDataProtectionCheck()
{
try
{
lock (fileLock)
{
LogHelper.WriteLogToFile("DeviceIdentifier | 开始使用频率数据保护检查");
var issues = new List<string>();
var repaired = new List<string>();
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<string>();
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;
}
}
/// <summary>
/// 根据优先级决定是否应该推送更新(仅适用于自动更新,版本修复功能不受影响)
/// </summary>
/// <param name="updateVersion">更新版本号</param>
/// <param name="releaseTime">新版本发布时间</param>
/// <param name="isAutoUpdate">是否为自动更新检查(默认true,false表示版本修复)</param>
/// <param name="currentVersionReleaseTime">当前版本发布时间</param>
/// <returns>是否应该推送更新</returns>
public static bool ShouldPushUpdate(string updateVersion, DateTime releaseTime, bool isAutoUpdate = true, DateTime? currentVersionReleaseTime = null)
{
try
{
// 判断更新类型(基于版本号)
var updateType = DetermineUpdateType(updateVersion);
// 如果不是自动更新(即版本修复),则应用不同的策略
if (!isAutoUpdate)
{
// 版本修复:立即允许,不受分级策略影响
LogHelper.WriteLogToFile($"DeviceIdentifier | 版本修复 - 版本: {updateVersion}, 类型: {updateType}, 结果: 允许");
return true;
}
var priority = GetUpdatePriority();
var frequency = GetUsageFrequency();
var stats = LoadUsageStats();
// 计算版本间的时间差
double daysBetweenVersions;
if (currentVersionReleaseTime.HasValue)
{
// 使用当前版本发布时间与新版本发布时间的差异
daysBetweenVersions = (releaseTime - currentVersionReleaseTime.Value).TotalDays;
}
else
{
// 如果没有当前版本发布时间,回退到使用新版本发布时间到现在的天数
daysBetweenVersions = (DateTime.Now - releaseTime).TotalDays;
}
// 计算最近活跃度(最后一次使用距今的天数)
var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays;
// 综合判断逻辑(仅适用于自动更新)
var shouldPush = ShouldPushUpdateComprehensive(priority, frequency, daysBetweenVersions, daysSinceLastUse, stats, updateType);
LogHelper.WriteLogToFile($"DeviceIdentifier | 自动更新推送判断 - 版本: {updateVersion}, 类型: {updateType}, " +
$"优先级: {priority}, 频率: {frequency}, 版本间隔: {daysBetweenVersions:F1}天, " +
$"最后使用: {daysSinceLastUse:F1}天前, 结果: {shouldPush}");
return shouldPush;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 判断是否推送更新失败: {ex.Message}", LogHelper.LogType.Error);
return true; // 出错时默认推送
}
}
/// <summary>
/// 更新类型枚举
/// </summary>
private enum UpdateType
{
Major, // 主版本更新 (x.0.0)
Minor, // 次版本更新 (x.y.0)
Patch, // 补丁更新 (x.y.z)
Hotfix, // 热修复更新
Unknown // 未知类型
}
/// <summary>
/// 根据版本号判断更新类型
/// </summary>
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;
}
}
/// <summary>
/// 综合时间和使用频率的自动更新推送判断逻辑(不影响版本修复)
/// </summary>
/// <param name="priority">用户更新优先级</param>
/// <param name="frequency">用户使用频率</param>
/// <param name="daysBetweenVersions">当前版本与新版本之间的天数差异</param>
/// <param name="daysSinceLastUse">距离最后使用的天数</param>
/// <param name="stats">使用统计数据</param>
/// <param name="updateType">更新类型</param>
/// <returns>是否应该推送更新</returns>
private static bool ShouldPushUpdateComprehensive(UpdatePriority priority, UsageFrequency frequency,
double daysBetweenVersions, 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 daysBetweenVersions >= 1; // 热修复版本间隔1天后推送
}
// 但如果是重度用户,仍然要适当推送
var baseDelay = isHeavyUser ? 7 : 14;
return daysBetweenVersions >= (baseDelay / urgencyMultiplier);
}
// 如果用户最近很活跃(3天内使用过)
if (daysSinceLastUse <= 3)
{
// 热修复立即推送给活跃用户
if (updateType == UpdateType.Hotfix)
{
return true;
}
// 结合使用频率和优先级判断
if (frequency == UsageFrequency.High || isHeavyUser)
{
return daysBetweenVersions >= Math.Max(0, 1 / urgencyMultiplier); // 高频用户优先推送
}
switch (priority)
{
case UpdatePriority.High:
return daysBetweenVersions >= Math.Max(0, 1 / urgencyMultiplier);
case UpdatePriority.Medium:
return daysBetweenVersions >= Math.Max(1, 2 / urgencyMultiplier);
case UpdatePriority.Low:
return daysBetweenVersions >= Math.Max(2, 3 / urgencyMultiplier);
}
}
// 中等活跃度用户(3-14天内使用过)
if (daysSinceLastUse <= 14)
{
// 热修复优先推送
if (updateType == UpdateType.Hotfix)
{
return daysBetweenVersions >= 1;
}
// 频繁用户优先推送
if (isFrequentUser && frequency == UsageFrequency.High)
{
return daysBetweenVersions >= Math.Max(1, 2 / urgencyMultiplier);
}
switch (priority)
{
case UpdatePriority.High:
return daysBetweenVersions >= Math.Max(1, 2 / urgencyMultiplier);
case UpdatePriority.Medium:
return daysBetweenVersions >= Math.Max(2, 4 / urgencyMultiplier);
case UpdatePriority.Low:
return daysBetweenVersions >= Math.Max(4, 7 / urgencyMultiplier);
}
}
// 较不活跃用户(14-30天内使用过)
// 对于低频率用户,进一步延迟推送
var delayMultiplier = frequency == UsageFrequency.Low ? 2 : 1;
switch (priority)
{
case UpdatePriority.High:
return daysBetweenVersions >= Math.Max(2, 3 * delayMultiplier / urgencyMultiplier);
case UpdatePriority.Medium:
return daysBetweenVersions >= Math.Max(4, 7 * delayMultiplier / urgencyMultiplier);
case UpdatePriority.Low:
return daysBetweenVersions >= Math.Max(7, 14 * delayMultiplier / urgencyMultiplier);
default:
return daysBetweenVersions >= 7;
}
}
/// <summary>
/// 根据更新类型获取紧急程度倍数(仅用于自动更新分级)
/// </summary>
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;
}
}
/// <summary>
/// 检查是否应该进行版本修复(不受分级策略影响)
/// </summary>
/// <param name="currentVersion">当前版本</param>
/// <param name="availableVersion">可用版本</param>
/// <returns>是否需要版本修复</returns>
public static bool ShouldPerformVersionFix(string currentVersion, string availableVersion)
{
try
{
// 版本修复功能不受使用频率分级策略影响,始终允许
LogHelper.WriteLogToFile($"DeviceIdentifier | 版本修复检查 - 当前版本: {currentVersion}, 可用版本: {availableVersion}, 结果: 允许");
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 版本修复检查失败: {ex.Message}", LogHelper.LogType.Error);
return true; // 出错时默认允许
}
}
/// <summary>
/// 获取设备信息摘要(用于调试)
/// </summary>
public static string GetDeviceInfoSummary()
{
try
{
var (launchCount, totalSeconds, avgSessionSeconds, priority) = GetUsageStats();
var frequency = GetUsageFrequency();
var stats = LoadUsageStats();
var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays;
return $"设备ID: {DeviceId}\n" +
$"启动次数: {launchCount}\n" +
$"总使用时长: {FormatDuration(totalSeconds)}\n" +
$"平均会话时长: {FormatDuration((long)avgSessionSeconds)}\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获取详细信息失败";
}
}
/// <summary>
/// 获取用户类型描述
/// </summary>
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<string>();
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) : "普通用户";
}
/// <summary>
/// 数据自检和修复
/// </summary>
public static bool PerformDataIntegrityCheck()
{
try
{
lock (fileLock)
{
// LogHelper.WriteLogToFile("DeviceIdentifier | 开始数据完整性检查");
var issues = new List<string>();
var repaired = new List<string>();
// 检查设备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;
}
}
/// <summary>
/// 获取使用频率数据保护状态摘要(强化版本)
/// </summary>
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($"总使用时长: {FormatDuration(stats.TotalUsageSeconds)}");
summary.AppendLine($"本周启动次数: {stats.WeeklyLaunchCount}");
summary.AppendLine($"本周使用时长: {FormatDuration(stats.WeeklyUsageSeconds)}");
summary.AppendLine($"上周启动次数: {stats.LastWeekLaunchCount}");
summary.AppendLine($"上周使用时长: {FormatDuration(stats.LastWeekUsageSeconds)}");
summary.AppendLine($"本周开始日期: {(stats.WeekStartDate != DateTime.MinValue ? stats.WeekStartDate.ToString("yyyy-MM-dd") : "")}");
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}";
}
}
/// <summary>
/// 计算使用频率数据保护强度评分
/// </summary>
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);
}
/// <summary>
/// 强制重建所有使用频率数据备份
/// </summary>
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;
}
}
/// <summary>
/// 获取数据保护状态摘要(保持向后兼容)
/// </summary>
public static string GetDataProtectionSummary()
{
return GetUsageDataProtectionSummary();
}
/// <summary>
/// 强制执行一次完整的数据保存操作(包括注册表)
/// </summary>
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;
}
// LogHelper.WriteLogToFile("DeviceIdentifier | 强制完整数据保存失败: 无法加载使用统计", LogHelper.LogType.Error);
return false;
}
}
catch (Exception ex)
{
// LogHelper.WriteLogToFile($"DeviceIdentifier | 强制完整数据保存失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 验证注册表中的数据是否存在
/// </summary>
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}";
}
}
/// <summary>
/// 立即执行一次数据保存并验证注册表写入
/// </summary>
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;
}
}
}
}