using Lierda.WPFHelper; using Microsoft.Extensions.DependencyInjection; using Serilog; using SlnMesnac.Config; using System; using System.Windows; using Microsoft.Extensions.Configuration; using SlnMesnac.Extensions; using SlnMesnac.Serilog; using System.Diagnostics; using System.IO; using System.Threading; using System.Linq; using System.Reflection; using TouchSocket.Sockets; using SlnMesnac.WPF.Attribute; using SlnMesnac.Rfid; namespace SlnMesnac.WPF { /// /// Interaction logic for App.xaml /// public partial class App : Application { private Mutex _instanceMutex = null; private LierdaCracker cracker = new LierdaCracker(); public static IServiceProvider ServiceProvider { get; private set; } // 实例唯一标识符 private static string _instanceId; // 实例数据目录 private static string _instanceDataPath; public new static App Current => (App)Application.Current; // 获取当前实例ID(静态属性) public static string InstanceId => _instanceId; // 获取当前实例的数据路径 public static string InstanceDataPath => _instanceDataPath; // Startup事件 protected override async void OnStartup(StartupEventArgs e) { try { this.DispatcherUnhandledException += App_DispatcherUnhandledException; //全局异常处理 #region 多实例初始化 // 从命令行参数获取实例ID(如果指定了) string customInstanceId = GetInstanceIdFromArgs(e.Args); // 生成实例ID _instanceId = !string.IsNullOrEmpty(customInstanceId) ? customInstanceId : GenerateInstanceId(); // 初始化实例数据目录 InitializeInstanceDataPath(); // 创建基于实例ID的互斥体名称,确保每个实例独立 string mutexName = $"Global\\{Process.GetCurrentProcess().ProcessName}_{_instanceId}"; bool createdNew; _instanceMutex = new Mutex(true, mutexName, out createdNew); if (!createdNew) { // 如果mutex已存在,说明相同实例ID的程序已经在运行 MessageBox.Show($"实例 {_instanceId} 已在运行中,无法重复启动!", "启动失败", MessageBoxButton.OK, MessageBoxImage.Warning); Environment.Exit(0); return; } #endregion cracker.Cracker(100); //设置GC回收间隔 base.OnStartup(e); // 设置ServiceCollection var services = new ServiceCollection(); ConfigureServices(services); // 配置服务 // 创建ServiceProvider ServiceProvider = services.BuildServiceProvider(); // 配置Serilog和其他扩展 ServiceProvider.UseSerilogExtensions(); // 获取AppConfig并更新实例特定的配置 var appConfig = ServiceProvider.GetService(); if (appConfig != null) { // 更新日志路径为实例专用路径 appConfig.logPath = Path.Combine(_instanceDataPath, "Logs"); Directory.CreateDirectory(appConfig.logPath); // 更新其他可能需要实例化的路径 appConfig.InstanceId = _instanceId; appConfig.InstanceDataPath = _instanceDataPath; // 根据实例ID调整数据库连接(如果需要) // AdjustDatabaseConnectionForInstance(appConfig); } Log.Information($"应用程序启动 - 实例ID: {_instanceId}, 进程ID: {Process.GetCurrentProcess().Id}"); Log.Information($"日志目录: {appConfig?.logPath}"); Log.Information($"数据目录: {_instanceDataPath}"); // 显示主窗口 var loginWindow = ServiceProvider.GetRequiredService(); loginWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen; // 在窗口标题显示实例信息 loginWindow.Title = $"{loginWindow.Title} [{_instanceId}]"; loginWindow.Show(); } catch (Exception exception) { Console.WriteLine(exception); throw; } } /// /// 从命令行参数获取实例ID /// private string GetInstanceIdFromArgs(string[] args) { if (args == null || args.Length == 0) return null; // 支持两种参数格式:/instance:ID 或 -instance=ID foreach (var arg in args) { if (arg.StartsWith("/instance:") || arg.StartsWith("-instance=")) { return arg.Contains(':') ? arg.Substring(arg.IndexOf(':') + 1) : arg.Substring(arg.IndexOf('=') + 1); } } return null; } /// /// 生成实例ID /// private string GenerateInstanceId() { string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss"); string random = new Random().Next(1000, 9999).ToString(); return $"{timestamp}_{random}"; } /// /// 初始化实例数据路径 /// private void InitializeInstanceDataPath() { string appName = Assembly.GetExecutingAssembly().GetName().Name; string baseDataRoot = GetDataRootPath(); _instanceDataPath = Path.Combine(baseDataRoot, appName, "Instances", _instanceId); Directory.CreateDirectory(_instanceDataPath); Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Data")); Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Logs")); Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Config")); Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Cache")); Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Temp")); SaveInstanceInfo(); CleanupOldInstances(); } /// /// 获取数据存储根路径 /// private string GetDataRootPath() { string envPath = Environment.GetEnvironmentVariable("SLNMESNAC_DATA_ROOT"); if (!string.IsNullOrEmpty(envPath) && Directory.Exists(envPath)) return envPath; return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); } /// /// 保存实例信息文件 /// private void SaveInstanceInfo() { try { var process = Process.GetCurrentProcess(); var info = new { InstanceId = _instanceId, StartTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), ProcessId = process.Id, ProcessName = process.ProcessName, MachineName = Environment.MachineName, UserName = Environment.UserName, WorkingDirectory = Environment.CurrentDirectory }; string infoFile = Path.Combine(_instanceDataPath, "instance.info"); File.WriteAllText(infoFile, $"Instance ID: {info.InstanceId}\r\n" + $"Start Time: {info.StartTime}\r\n" + $"Process ID: {info.ProcessId}\r\n" + $"Process Name: {info.ProcessName}\r\n" + $"Machine: {info.MachineName}\r\n" + $"User: {info.UserName}\r\n" + $"Working Dir: {info.WorkingDirectory}"); } catch (Exception ex) { Debug.WriteLine($"保存实例信息失败: {ex.Message}"); } } /// /// 清理过期的实例数据 /// private void CleanupOldInstances(int keepDays = 3) { try { string appName = Assembly.GetExecutingAssembly().GetName().Name; string instancesRoot = Path.Combine( GetDataRootPath(), appName, "Instances" ); if (!Directory.Exists(instancesRoot)) return; DateTime cutoffTime = DateTime.Now.AddDays(-keepDays); var instanceDirs = Directory.GetDirectories(instancesRoot); foreach (var dir in instanceDirs) { try { var dirInfo = new DirectoryInfo(dir); var infoFile = Path.Combine(dir, "instance.info"); if (File.Exists(infoFile)) { // 读取启动时间 var content = File.ReadAllText(infoFile); var startTimeLine = content.Split('\n') .FirstOrDefault(line => line.StartsWith("Start Time:")); if (!string.IsNullOrEmpty(startTimeLine)) { string timeStr = startTimeLine.Substring(11).Trim(); if (DateTime.TryParse(timeStr, out DateTime startTime)) { if (startTime < cutoffTime) { Directory.Delete(dir, true); Debug.WriteLine($"已清理过期实例: {dirInfo.Name}"); } } } } else if (dirInfo.CreationTime < cutoffTime) { // 如果没有信息文件,使用目录创建时间 Directory.Delete(dir, true); Debug.WriteLine($"已清理过期实例: {dirInfo.Name}"); } } catch (Exception ex) { Debug.WriteLine($"清理实例 {dir} 失败: {ex.Message}"); } } } catch (Exception ex) { Debug.WriteLine($"清理实例数据失败: {ex.Message}"); } } /// /// ConfigureServices /// private void ConfigureServices(IServiceCollection services) { // 注册AppConfig services.AddSingleton(provider => { var configurationBuilder = new ConfigurationBuilder() .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); // 添加实例特定的配置文件(如果存在) string instanceConfig = Path.Combine(_instanceDataPath, "Config", "appsettings.instance.json"); if (File.Exists(instanceConfig)) { configurationBuilder.AddJsonFile(instanceConfig, optional: true, reloadOnChange: true); } IConfiguration configuration = configurationBuilder.Build(); var appConfig = configuration.GetSection("AppConfig").Get(); // 设置实例特定的属性 if (appConfig != null) { appConfig.InstanceId = _instanceId; appConfig.InstanceDataPath = _instanceDataPath; } return appConfig; }); //services.AddSingleton(); // 加载程序集 Assembly[] assemblies = { Assembly.LoadFrom("SlnMesnac.Repository.dll"), Assembly.LoadFrom("SlnMesnac.Plc.dll"), Assembly.LoadFrom("SlnMesnac.Rfid.dll"), Assembly.LoadFrom("SlnMesnac.Common.dll"), Assembly.LoadFrom("SlnMesnac.TouchSocket.dll"), Assembly.LoadFrom("SlnMesnac.Business.dll"), Assembly.LoadFrom("SlnMesnac.Generate.dll") }; // 扫描并注册程序集中的服务 services.Scan(scan => scan.FromAssemblies(assemblies) .AddClasses() .AsImplementedInterfaces() .AsSelf() .WithTransientLifetime()); // 注册TcpService - 需要确保每个实例使用不同的端口 services.AddSingleton(provider => { var tcpService = new TcpService(); // 根据实例ID调整端口配置 // 例如:tcpService.Port = GetInstanceSpecificPort(_instanceId); return tcpService; }); services.AddLogging(x => x.AddSerilog()); // 扫描并注册带有特性的窗口和服务 services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.WithAttribute()) .AsSelf() .WithSingletonLifetime()); services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.WithAttribute()) .AsSelf() .WithTransientLifetime()); // 注册ORM services.AddSqlSugarSetup(); services.AddRfidFactorySetup(); } // Exit事件 protected override void OnExit(ExitEventArgs e) { base.OnExit(e); Log.Information($"实例 {_instanceId} 退出,退出时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); // 释放互斥体 try { _instanceMutex?.ReleaseMutex(); _instanceMutex?.Dispose(); } catch (Exception ex) { Debug.WriteLine($"释放互斥体失败: {ex.Message}"); } } private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) { string errorMsg = $"[实例 {_instanceId}] 发生未处理异常: {e.Exception.Message}"; MessageBox.Show(errorMsg, "应用程序错误", MessageBoxButton.OK, MessageBoxImage.Error); Log.Error(e.Exception, $"实例 {_instanceId} 全局异常: {e.Exception.Message}"); // 保存异常信息到实例日志目录 try { string errorLogFile = Path.Combine(_instanceDataPath, "Logs", $"crash_{DateTime.Now:yyyyMMdd_HHmmss}.log"); File.WriteAllText(errorLogFile, $"实例ID: {_instanceId}\r\n" + $"异常时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\r\n" + $"异常信息: {e.Exception.Message}\r\n" + $"堆栈跟踪:\r\n{e.Exception.StackTrace}"); } catch { // 忽略文件保存失败 } e.Handled = true; } // 静态辅助方法 /// /// 启动一个新的应用程序实例 /// public static void StartNewInstance(string instanceId = null) { try { string exePath = Process.GetCurrentProcess().MainModule?.FileName; if (!string.IsNullOrEmpty(exePath)) { string arguments = string.Empty; if (!string.IsNullOrEmpty(instanceId)) { arguments = $"/instance:{instanceId}"; } ProcessStartInfo startInfo = new ProcessStartInfo { FileName = exePath, Arguments = arguments, UseShellExecute = true }; Process.Start(startInfo); } } catch (Exception ex) { MessageBox.Show($"启动新实例失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 获取所有实例的目录 /// public static string[] GetAllInstanceDirectories() { string appName = Assembly.GetExecutingAssembly().GetName().Name; string instancesRoot = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), appName, "Instances" ); return Directory.Exists(instancesRoot) ? Directory.GetDirectories(instancesRoot) : Array.Empty(); } /// /// 获取所有运行中的实例信息 /// public static InstanceInfo[] GetAllRunningInstances() { var instanceDirs = GetAllInstanceDirectories(); var instances = new System.Collections.Generic.List(); foreach (var dir in instanceDirs) { try { string infoFile = Path.Combine(dir, "instance.info"); if (File.Exists(infoFile)) { var content = File.ReadAllLines(infoFile); var instanceId = Path.GetFileName(dir); var info = new InstanceInfo { Id = instanceId, DataPath = dir, CreationTime = Directory.GetCreationTime(dir) }; instances.Add(info); } } catch { // 忽略无法读取的实例 } } return instances.ToArray(); } /// /// 实例信息类 /// public class InstanceInfo { public string Id { get; set; } public string DataPath { get; set; } public DateTime CreationTime { get; set; } } } }