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; }
}
}
}