将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。

This commit is contained in:
QI Mingxuan
2026-04-16 17:31:13 +08:00
parent 6ec4c3ddaa
commit 2bd6e566c3
581 changed files with 74600 additions and 222 deletions
+34
View File
@@ -0,0 +1,34 @@
namespace XP.Common.Dump.Configs
{
/// <summary>
/// Dump 功能配置实体(从 App.config 读取)
/// Dump feature configuration entity (loaded from App.config)
/// </summary>
public class DumpConfig
{
/// <summary>
/// Dump 文件存储路径 | Dump file storage path
/// </summary>
public string StoragePath { get; set; } = @"D:\XplorePlane\Dump";
/// <summary>
/// 是否启用定时触发 | Enable scheduled trigger
/// </summary>
public bool EnableScheduledDump { get; set; } = false;
/// <summary>
/// 定时触发间隔(分钟)| Scheduled trigger interval (minutes)
/// </summary>
public int ScheduledIntervalMinutes { get; set; } = 60;
/// <summary>
/// Mini Dump 文件大小上限(MB| Mini Dump file size limit (MB)
/// </summary>
public long MiniDumpSizeLimitMB { get; set; } = 100;
/// <summary>
/// 文件保留天数 | File retention days
/// </summary>
public int RetentionDays { get; set; } = 7;
}
}
+23
View File
@@ -0,0 +1,23 @@
namespace XP.Common.Dump.Configs
{
/// <summary>
/// Dump 触发类型 | Dump trigger type
/// </summary>
public enum DumpTriggerType
{
/// <summary>
/// 崩溃触发 | Crash trigger
/// </summary>
Crash,
/// <summary>
/// 定时触发 | Scheduled trigger
/// </summary>
Scheduled,
/// <summary>
/// 手动触发 | Manual trigger
/// </summary>
Manual
}
}
@@ -0,0 +1,69 @@
using System;
using System.IO;
using System.Linq;
using XP.Common.Dump.Configs;
using XP.Common.Logging.Interfaces;
namespace XP.Common.Dump.Implementations
{
/// <summary>
/// Dump 文件自动清理组件,按保留天数策略删除过期文件
/// Auto cleanup component for dump files, deletes expired files based on retention policy
/// </summary>
internal class DumpCleaner
{
private readonly DumpConfig _config;
private readonly ILoggerService _logger;
public DumpCleaner(DumpConfig config, ILoggerService logger)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
_logger = logger?.ForModule<DumpCleaner>() ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 执行清理操作:删除超过保留天数的 .dmp 文件
/// Execute cleanup: delete .dmp files older than retention days
/// </summary>
public void CleanExpiredFiles()
{
try
{
if (!Directory.Exists(_config.StoragePath))
{
_logger.Debug("存储目录不存在,跳过清理:{Path} | Storage directory does not exist, skipping cleanup: {Path}", _config.StoragePath);
return;
}
var dmpFiles = Directory.GetFiles(_config.StoragePath, "*.dmp");
var cutoffTime = DateTime.Now.AddDays(-_config.RetentionDays);
var deletedCount = 0;
foreach (var filePath in dmpFiles)
{
try
{
var creationTime = File.GetCreationTime(filePath);
if (creationTime < cutoffTime)
{
var fileName = Path.GetFileName(filePath);
File.Delete(filePath);
deletedCount++;
_logger.Info("已删除过期 Dump 文件:{FileName} | Deleted expired dump file: {FileName}", fileName);
}
}
catch (Exception ex)
{
_logger.Error(ex, "删除 Dump 文件失败:{FilePath} | Failed to delete dump file: {FilePath}", filePath);
}
}
_logger.Info("Dump 清理完成,共删除 {Count} 个文件 | Dump cleanup completed, {Count} files deleted", deletedCount);
}
catch (Exception ex)
{
_logger.Error(ex, "Dump 清理过程发生异常:{Message} | Dump cleanup error: {Message}", ex.Message);
}
}
}
}
@@ -0,0 +1,321 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using XP.Common.Dump.Configs;
using XP.Common.Dump.Interfaces;
using XP.Common.Dump.Native;
using XP.Common.Logging.Interfaces;
namespace XP.Common.Dump.Implementations
{
/// <summary>
/// Dump 文件管理服务实现,负责 Dump 生成、崩溃事件订阅、定时调度和清理
/// Dump file management service implementation, handles dump generation, crash event subscription, scheduled tasks and cleanup
/// </summary>
public class DumpService : IDumpService
{
private readonly DumpConfig _config;
private readonly ILoggerService _logger;
private readonly DumpCleaner _cleaner;
private Timer? _scheduledTimer;
private Timer? _cleanupTimer;
private bool _isStarted;
private bool _disposed;
/// <summary>
/// 默认存储路径 | Default storage path
/// </summary>
private const string DefaultStoragePath = @"D:\XplorePlane\Dump";
public DumpService(DumpConfig config, ILoggerService logger)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
_logger = logger?.ForModule<DumpService>() ?? throw new ArgumentNullException(nameof(logger));
_cleaner = new DumpCleaner(config, logger);
}
/// <summary>
/// 生成 Dump 文件名(纯函数)| Generate dump file name (pure function)
/// 格式:XplorePlane_{yyyyMMdd_HHmm}_{TriggerType}.dmp
/// </summary>
/// <param name="triggerType">触发类型 | Trigger type</param>
/// <param name="timestamp">时间戳 | Timestamp</param>
/// <returns>文件名 | File name</returns>
internal static string GenerateFileName(DumpTriggerType triggerType, DateTime timestamp)
{
return $"XplorePlane_{timestamp:yyyyMMdd_HHmm}_{triggerType}.dmp";
}
/// <summary>
/// 验证 Mini Dump 文件大小是否在限制范围内 | Validate Mini Dump file size is within limit
/// </summary>
/// <param name="fileSizeBytes">文件大小(字节)| File size in bytes</param>
/// <param name="sizeLimitMB">大小限制(MB| Size limit in MB</param>
/// <returns>true 表示在限制内 | true if within limit</returns>
internal static bool ValidateFileSize(long fileSizeBytes, long sizeLimitMB)
{
return fileSizeBytes <= sizeLimitMB * 1024 * 1024;
}
/// <summary>
/// 写入 Dump 文件的核心方法 | Core method to write dump file
/// </summary>
/// <param name="triggerType">触发类型 | Trigger type</param>
/// <param name="isMiniDump">是否为 Mini Dump | Whether it is a Mini Dump</param>
/// <returns>生成的文件完整路径,失败返回 null | Full path of generated file, null on failure</returns>
private string? WriteDump(DumpTriggerType triggerType, bool isMiniDump)
{
try
{
// 非手动触发请求 Full Dump 时,降级为 Mini Dump | Downgrade to Mini Dump for non-manual Full Dump requests
if (!isMiniDump && triggerType != DumpTriggerType.Manual)
{
_logger.Warn("非手动触发不允许生成 Full Dump,已降级为 Mini Dump{TriggerType} | Non-manual trigger cannot generate Full Dump, downgraded to Mini Dump: {TriggerType}", triggerType);
isMiniDump = true;
}
var fileName = GenerateFileName(triggerType, DateTime.Now);
var filePath = Path.Combine(_config.StoragePath, fileName);
// 确定 Dump 类型标志 | Determine dump type flags
uint dumpType = isMiniDump
? (uint)(MiniDumpType.MiniDumpWithDataSegs | MiniDumpType.MiniDumpWithHandleData | MiniDumpType.MiniDumpWithThreadInfo)
: (uint)MiniDumpType.MiniDumpWithFullMemory;
var process = Process.GetCurrentProcess();
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
{
bool success = NativeMethods.MiniDumpWriteDump(
process.Handle,
(uint)process.Id,
fileStream.SafeFileHandle,
dumpType,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero);
if (!success)
{
var errorCode = Marshal.GetLastWin32Error();
_logger.Error(null!, "MiniDumpWriteDump 调用失败,错误码:{ErrorCode} | MiniDumpWriteDump failed, error code: {ErrorCode}", errorCode);
// 清理失败的文件 | Clean up failed file
try { File.Delete(filePath); } catch { }
return null;
}
}
// Mini Dump 文件大小检查 | Mini Dump file size check
if (isMiniDump)
{
var fileInfo = new FileInfo(filePath);
if (!ValidateFileSize(fileInfo.Length, _config.MiniDumpSizeLimitMB))
{
_logger.Warn("Mini Dump 文件超过大小限制({SizeMB}MB),已删除:{FilePath} | Mini Dump file exceeds size limit ({SizeMB}MB), deleted: {FilePath}",
_config.MiniDumpSizeLimitMB, filePath);
File.Delete(filePath);
return null;
}
}
var dumpTypeStr = isMiniDump ? "Mini" : "Full";
_logger.Info("Dump 文件已生成:{FilePath},触发类型:{TriggerType}Dump 类型:{DumpType} | Dump file generated: {FilePath}, trigger: {TriggerType}, type: {DumpType}",
filePath, triggerType, dumpTypeStr);
return filePath;
}
catch (IOException ex)
{
_logger.Error(ex, "Dump 文件写入失败:{Message} | Dump file write failed: {Message}", ex.Message);
return null;
}
catch (UnauthorizedAccessException ex)
{
_logger.Error(ex, "Dump 文件写入权限不足:{Message} | Dump file write permission denied: {Message}", ex.Message);
return null;
}
catch (Exception ex)
{
_logger.Error(ex, "Dump 文件生成异常:{Message} | Dump file generation error: {Message}", ex.Message);
return null;
}
}
#region | Manual trigger methods
/// <inheritdoc />
public string? CreateMiniDump()
{
_logger.Info("手动触发 Mini Dump 生成 | Manually triggering Mini Dump generation");
var filePath = WriteDump(DumpTriggerType.Manual, isMiniDump: true);
if (filePath != null)
{
_logger.Info("手动 Mini Dump 已生成:{FilePath} | Manual Mini Dump generated: {FilePath}", filePath);
}
return filePath;
}
/// <inheritdoc />
public string? CreateFullDump()
{
_logger.Info("手动触发 Full Dump 生成 | Manually triggering Full Dump generation");
var filePath = WriteDump(DumpTriggerType.Manual, isMiniDump: false);
if (filePath != null)
{
_logger.Info("手动 Full Dump 已生成:{FilePath} | Manual Full Dump generated: {FilePath}", filePath);
}
return filePath;
}
#endregion
#region | Lifecycle management
/// <inheritdoc />
public void Start()
{
if (_isStarted) return;
_logger.Info("Dump 服务启动中 | Dump service starting");
// 确保存储目录存在 | Ensure storage directory exists
EnsureStorageDirectory();
// 订阅崩溃事件 | Subscribe to crash events
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
// 启动定时 Dump 计时器 | Start scheduled dump timer
if (_config.EnableScheduledDump)
{
var intervalMs = _config.ScheduledIntervalMinutes * 60 * 1000;
_scheduledTimer = new Timer(OnScheduledDumpCallback, null, intervalMs, intervalMs);
_logger.Info("定时 Dump 已启用,间隔:{Interval} 分钟 | Scheduled dump enabled, interval: {Interval} minutes", _config.ScheduledIntervalMinutes);
}
// 启动每日清理计时器(每24小时执行一次)| Start daily cleanup timer (every 24 hours)
var dailyMs = 24 * 60 * 60 * 1000;
_cleanupTimer = new Timer(OnCleanupCallback, null, dailyMs, dailyMs);
// 立即执行一次清理 | Execute cleanup immediately
_cleaner.CleanExpiredFiles();
_isStarted = true;
_logger.Info("Dump 服务已启动 | Dump service started");
}
/// <inheritdoc />
public void Stop()
{
if (!_isStarted) return;
_logger.Info("Dump 服务停止中 | Dump service stopping");
// 取消事件订阅 | Unsubscribe from events
AppDomain.CurrentDomain.UnhandledException -= OnUnhandledException;
TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException;
// 停止所有计时器 | Stop all timers
_scheduledTimer?.Change(Timeout.Infinite, Timeout.Infinite);
_cleanupTimer?.Change(Timeout.Infinite, Timeout.Infinite);
_isStarted = false;
_logger.Info("Dump 服务已停止 | Dump service stopped");
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed) return;
Stop();
_scheduledTimer?.Dispose();
_scheduledTimer = null;
_cleanupTimer?.Dispose();
_cleanupTimer = null;
_disposed = true;
}
#endregion
#region | Internal methods
/// <summary>
/// 确保存储目录存在,不存在则创建,创建失败回退默认路径
/// Ensure storage directory exists, create if not, fallback to default on failure
/// </summary>
private void EnsureStorageDirectory()
{
try
{
if (!Directory.Exists(_config.StoragePath))
{
Directory.CreateDirectory(_config.StoragePath);
_logger.Info("已创建存储目录:{Path} | Created storage directory: {Path}", _config.StoragePath);
}
}
catch (Exception ex)
{
_logger.Error(ex, "存储目录创建失败:{Path},回退到默认路径 | Storage directory creation failed: {Path}, falling back to default", _config.StoragePath);
_config.StoragePath = DefaultStoragePath;
try
{
if (!Directory.Exists(DefaultStoragePath))
{
Directory.CreateDirectory(DefaultStoragePath);
_logger.Info("已创建默认存储目录:{Path} | Created default storage directory: {Path}", DefaultStoragePath);
}
}
catch (Exception fallbackEx)
{
_logger.Fatal(fallbackEx, "默认存储目录创建也失败:{Path} | Default storage directory creation also failed: {Path}", DefaultStoragePath);
}
}
}
/// <summary>
/// 未处理异常回调 | Unhandled exception callback
/// </summary>
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
_logger.Info("检测到未处理异常,正在生成崩溃 Dump | Unhandled exception detected, generating crash dump");
WriteDump(DumpTriggerType.Crash, isMiniDump: true);
}
/// <summary>
/// 未观察到的 Task 异常回调 | Unobserved task exception callback
/// </summary>
private void OnUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
_logger.Info("检测到未观察的 Task 异常,正在生成崩溃 Dump | Unobserved task exception detected, generating crash dump");
WriteDump(DumpTriggerType.Crash, isMiniDump: true);
}
/// <summary>
/// 定时 Dump 回调 | Scheduled dump callback
/// </summary>
private void OnScheduledDumpCallback(object? state)
{
_logger.Debug("定时 Dump 触发 | Scheduled dump triggered");
WriteDump(DumpTriggerType.Scheduled, isMiniDump: true);
}
/// <summary>
/// 清理回调 | Cleanup callback
/// </summary>
private void OnCleanupCallback(object? state)
{
_logger.Debug("定时清理触发 | Scheduled cleanup triggered");
_cleaner.CleanExpiredFiles();
}
#endregion
}
}
+32
View File
@@ -0,0 +1,32 @@
using System;
namespace XP.Common.Dump.Interfaces
{
/// <summary>
/// Dump 文件管理服务接口 | Dump file management service interface
/// </summary>
public interface IDumpService : IDisposable
{
/// <summary>
/// 手动触发 Mini Dump 生成 | Manually trigger Mini Dump generation
/// </summary>
/// <returns>生成的 Dump 文件完整路径,失败返回 null | Full path of generated dump file, null on failure</returns>
string? CreateMiniDump();
/// <summary>
/// 手动触发 Full Dump 生成 | Manually trigger Full Dump generation
/// </summary>
/// <returns>生成的 Dump 文件完整路径,失败返回 null | Full path of generated dump file, null on failure</returns>
string? CreateFullDump();
/// <summary>
/// 启动服务(订阅崩溃事件、启动定时任务和清理任务)| Start service (subscribe crash events, start scheduled and cleanup tasks)
/// </summary>
void Start();
/// <summary>
/// 停止服务(取消定时任务、取消事件订阅)| Stop service (cancel scheduled tasks, unsubscribe events)
/// </summary>
void Stop();
}
}
+37
View File
@@ -0,0 +1,37 @@
using System;
namespace XP.Common.Dump.Native
{
/// <summary>
/// MiniDump 类型标志,用于 MiniDumpWriteDump 的 dumpType 参数
/// MiniDump type flags for MiniDumpWriteDump dumpType parameter
/// </summary>
[Flags]
internal enum MiniDumpType : uint
{
/// <summary>
/// 仅包含基本信息 | Basic information only
/// </summary>
MiniDumpNormal = 0x00000000,
/// <summary>
/// 包含数据段信息 | Include data segment information
/// </summary>
MiniDumpWithDataSegs = 0x00000001,
/// <summary>
/// 包含完整内存信息(Full Dump 使用)| Include full memory (used for Full Dump)
/// </summary>
MiniDumpWithFullMemory = 0x00000002,
/// <summary>
/// 包含句柄信息 | Include handle information
/// </summary>
MiniDumpWithHandleData = 0x00000004,
/// <summary>
/// 包含线程信息 | Include thread information
/// </summary>
MiniDumpWithThreadInfo = 0x00001000,
}
}
+25
View File
@@ -0,0 +1,25 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace XP.Common.Dump.Native
{
/// <summary>
/// Windows 原生方法 P/Invoke 声明 | Windows native method P/Invoke declarations
/// </summary>
internal static class NativeMethods
{
/// <summary>
/// 将进程的 Mini Dump 写入指定文件 | Write a Mini Dump of the process to the specified file
/// </summary>
[DllImport("dbghelp.dll", SetLastError = true)]
internal static extern bool MiniDumpWriteDump(
IntPtr hProcess,
uint processId,
SafeHandle hFile,
uint dumpType,
IntPtr exceptionParam,
IntPtr userStreamParam,
IntPtr callbackParam);
}
}