兼容win7版应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

369 lines
16 KiB

using AIK.Common.Interface;
using AIK.Models.GuageModels;
using AIK.Service.Extensions;
using AIK.Service.IService;
using AIK.Service.Service;
using AIK.ViewModels;
using AIK.ViewModels.ChipWriteStart;
using AIK.ViewModels.CloudData;
using AIK.ViewModels.CommonViewModel;
using AIK.ViewModels.HomePage;
using AIK.ViewModels.Integral;
using AIK.ViewModels.Membership;
using AIK.ViewModels.RemoteControlDetector;
using AIK.ViewModels.RemoteCopy;
using AIK.ViewModels.RemoteGeneration;
using AIK.ViewModels.TirePressure;
using AIK.ViewModels.UpdateCenter;
using AIK.ViewModels.UserKeyRecord;
using AIK.Views.AccountPages;
using AIK.Views.MenuPages;
using AIK.Views.MenuPages.ChipGeneration;
using AIK.Views.MenuPages.ChipWriteStart;
using AIK.Views.MenuPages.CloudData;
using AIK.Views.MenuPages.CommonPage;
using AIK.Views.MenuPages.Home;
using AIK.Views.MenuPages.Integral;
using AIK.Views.MenuPages.RemoteControlDetector;
using AIK.Views.MenuPages.RemoteCopy;
using AIK.Views.MenuPages.RemoteGeneration;
using AIK.Views.MenuPages.TirePressure;
using AIK.Views.MenuPages.UpdateCenter;
using AIK.Views.UIServices;
using AIK.Views.UpdatePages;
using AIKKey.Infrastructure;
using AutoUpdaterDotNET;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Extensions.Hosting;
using NLog.Extensions.Logging;
using System;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Navigation;
namespace AIKKey
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private static IHost? _host;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private UpdateWindow _updateWindow;
public static IHost Host => _host
??= CreateHostBuilder(Environment.GetCommandLineArgs()).Build();
private static Mutex _mutex;
public static IServiceProvider Services => Host.Services;
private IServiceProvider? _serviceProvider;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
logging.AddNLog(context.Configuration);
})
.UseNLog()
.ConfigureServices(ConfigureServices);
private static void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
// 注册视图模型
services.AddSingleton<MainViewModel>();
services.AddTransient<RemoteCopyViewModel>();
services.AddSingleton<KeyModelViewModel>();
services.AddSingleton<RemoteGenerationViewModel>();
services.AddSingleton<RemoteControlDetectorViewModel>();
services.AddSingleton<SettingsViewModel>();
services.AddSingleton<VideoPlayerViewModel>();
services.AddSingleton<SemiCircleGaugeViewModel>();
services.AddSingleton<HomePageViewModel>();
services.AddTransient<UpdateCenterViewModel>();
services.AddTransient<UserKeyRecordViewModel>();
services.AddTransient<IntegralViewModel>();
services.AddTransient<IntegralRechargeViewModel>();
services.AddTransient<PaymentQrCodeViewModel>();
services.AddTransient<MembershipOpenViewModel>();
services.AddSingleton<CloudDataViewModel>();
services.AddSingleton<ChipWriteStartViewModel>();
services.AddSingleton<TirePressureViewModel>();
#region 注册服务
// 基础服务
services.AddSingleton<IConfigurationService, ConfigurationService>();
services.AddSingleton<ILogService, LogService>();
services.AddSingleton<IHttpClientService, HttpClientService>();
services.AddSingleton<IRetryPolicyService, RetryPolicyService>();
services.AddSingleton<IDataCacheService, SqliteCacheService>();
services.AddSingleton<IAikDataService, AikDataService>();
services.AddSingleton<IMembershipService, MembershipService>();
services.AddSingleton<IPaymentOrderService, PaymentOrderService>();
services.AddSingleton<ISettingsService, SettingsService>();
services.AddSingleton<IUserSessionCoordinator, UserSessionCoordinator>();
//更新服务
services.AddSingleton<IUpdateService, UpdateService>();
//工厂(文件操作obd/bin)
services.AddSingleton<FileOBDService>();
services.AddSingleton<FileBinService>();
services.AddSingleton<FileCloudDataService>();
services.AddSingleton<IFileServiceFactory>(sp => new FileServiceFactory(
sp.GetRequiredService<FileOBDService>(),
sp.GetRequiredService<FileBinService>(),
sp.GetRequiredService<FileCloudDataService>()
));
//文件基础服务(复制、解压)
services.AddSingleton<IFileOperationService, FileOperationService>();
//窗体弹窗
services.AddSingleton<IWindowFactory, WindowFactory>();
services.AddSingleton<IWindowService, WindowService>();
services.AddSingleton<IModalDialogService, ModalDialogService>();
//二维码
services.AddSingleton<IQRCodeService, QRCodeService>();
//业务服务
services.AddSingleton<ICarfactoryService, CarfactoryService>();
services.AddSingleton<IVehDataService, VehDataService>();
services.AddSingleton<IImageCacheService, RemoteImageCacheService>();
services.AddSingleton<ILocalizationService, LocalizationService>();
//Hid通信
services.AddSingleton<IHidCommunicationService, HidCommunicationService>();
// 文件选择
services.AddSingleton<IFileDialogService, WpfFileDialogService>();
#endregion
//注册模型
services.AddSingleton<GaugeModel>(); // 单例模式,整个应用共享一个实例
// 注册 Messenger(单例)
services.AddSingleton<IMessenger>(provider => WeakReferenceMessenger.Default);
// 注册窗口
services.AddSingleton<MainWindow>();
services.AddSingleton<RemoteCopyPage>();
services.AddSingleton<KeyModelPage>();
services.AddSingleton<RemoteGenerationPage>();
services.AddTransient<RemoteControlDetectorPage>();
services.AddSingleton<SettingsPage>();
services.AddSingleton<VideoPopupWindow>();
services.AddSingleton<SemiCircleGauge>();
services.AddSingleton<HomePage>();
services.AddTransient<UpdateCenterPage>();
services.AddTransient<IntegralRecordPage>();
services.AddSingleton<CloudDataPage>();
services.AddSingleton<ChipWriteStartPage>();
services.AddSingleton<TirePressurePage>();
// 注册 HttpClientFactory
services.AddHttpClient();
}
private void RegisterWindowMappings()
{
var windowService = Services.GetRequiredService<IWindowService>();
// 注册功能窗口映射
windowService.RegisterWindow(WindowKey.VideoPopup, typeof(VideoPopupWindow), typeof(VideoPlayerViewModel));
}
protected override async void OnStartup(StartupEventArgs e)
{
try
{
string appName = "AIKKeyWin7_Mutex"; // 替换为你的应用名
#if CN
appName = "AIKKeyWin7_Mutex";
#elif TEST
appName = "AIKKeyTESTWin7_Mutex";
#else
appName = "AIKKeyGlobalWin7_Mutex";
#endif
_mutex = new Mutex(true, appName, out bool createdNew);
if (!createdNew)
{
MessageBox.Show("应用已在运行!");
Current.Shutdown();
return;
}
// 系统级操作(Win7 硬件重扫 CM_ReScan_DevNode 等)需要管理员权限:
// 记录权限授予是否生效,便于排查"重扫未生效/权限未开启"问题。
//Logger.Info(UsbRescanHelper.IsAdministrator()
// ? "权限授予成功:当前进程已以管理员权限运行(硬件重扫等系统操作可用)"
// : "权限未授予:当前进程非管理员权限,硬件重扫(CM_ReScan_DevNode)等系统操作将无法执行");
// Win7 下 .NET Framework 默认仅协商 TLS 1.0,而服务器(key.aik518.com)只接受 TLS 1.2+,
// 必须显式启用 TLS 1.2,否则所有 HTTPS/wss 请求(扫码登录、业务接口、图片下载)都会握手失败。
// 放在 base.OnStartup 之前:StartupUri 创建 MainWindow 时会立即发起扫码登录连接。
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls;
//// 系统级兜底:写入 SchUseStrongCrypto + SChannel TLS 1.2 注册表(需要管理员权限,
//// 首次管理员运行一次后重启生效;无权限时只记日志,不阻塞启动)。
//if (TlsRegistryHelper.IsWindows7)
//{
// TlsRegistryHelper.EnsureTls12Enabled(out bool changed, out string error);
// if (changed)
// {
// Logger.Info("已写入 TLS 1.2 注册表配置(SchUseStrongCrypto + SChannel),需要重启系统后生效。");
// }
// else if (!string.IsNullOrEmpty(error))
// {
// Logger.Warn("TLS 1.2 注册表配置未写入: {Error}", error);
// }
// // 无论是否本次写入,都记录当前注册表实际状态,便于定位"配置了但未生效"的问题
// Logger.Info("TLS 1.2 注册表当前状态: {Status}", TlsRegistryHelper.GetRegistryStatus());
//}
//// Win7 启动自检:用 SslStream 直接探测 TLS 1.2 握手,区分"网络不通"与"系统 SChannel 未启用 TLS 1.2"
//_ = RunTls12StartupProbeAsync();
// 初始化全局异常处理
base.OnStartup(e);
SetupExceptionHandling();
await Host.StartAsync();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// 配置 CommunityToolkit.Mvvm 的 Ioc
Ioc.Default.ConfigureServices(Services);
IUpdateService updateService= Services.GetRequiredService<IUpdateService>();
//await updateService.StartUpdateProcess();
await updateService.CheckForUpdate();
RegisterWindowMappings();
// 后台预加热 WebView2 环境,加速 AI 客服窗口首次打开
_ = PreWarmWebView2Async();
}
catch (Exception ex)
{
Logger.Fatal(ex, "应用程序启动失败");
MessageBox.Show($"应用程序启动失败: {ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown();
}
}
/// <summary>
/// 后台预创建 WebView2 环境,避免 AI 客服窗口打开时实时初始化。
/// WebView2 运行时内部会缓存已创建的环境,后续 EnsureCoreWebView2Async 直接复用。
/// </summary>
private static async Task PreWarmWebView2Async()
{
try
{
await Task.Run(() => Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync());
}
catch
{
// 预热失败不影响主流程,窗口打开时还会再尝试 EnsureCoreWebView2Async
}
}
/// <summary>
/// Win7 启动自检:对业务 API 服务器做一次 TLS 1.2 握手探测并记入日志。
/// 用于区分"网络不通"与"系统 SChannel 未启用 TLS 1.2"(后者在 Win7 上只能通过
/// 打补丁 + 写注册表 + 重启系统解决,代码层 ServicePointManager/SslProtocols 无法绕过)。
/// </summary>
private static async Task RunTls12StartupProbeAsync()
{
try
{
string host;
int port = 443;
try
{
var configService = Services.GetRequiredService<IConfigurationService>();
var baseUrl = configService.ApiSettings.BaseUrl;
var uri = new Uri(baseUrl.Contains("://") ? baseUrl : "https://" + baseUrl);
host = uri.Host;
port = uri.Port > 0 ? uri.Port : 443;
}
catch
{
host = "key.aikkey.cn"; // 兜底,与日志中实际访问的服务器一致
}
var result = await TlsRegistryHelper.ProbeTls12HandshakeAsync(host, port);
Logger.Info("TLS 1.2 启动自检: {Result}", result);
}
catch (Exception ex)
{
Logger.Error(ex, "TLS 1.2 启动自检失败");
}
}
protected override async void OnExit(ExitEventArgs e)
{
_mutex?.ReleaseMutex();
base.OnExit(e);
using (Host)
{
await Host.StopAsync(TimeSpan.FromSeconds(5));
}
}
private void SetupExceptionHandling()
{
// UI线程异常
DispatcherUnhandledException += (s, e) =>
{
Logger.Fatal(e.Exception, "UI线程未处理异常");
e.Handled = true;
ShowErrorDialog("应用程序错误", e.Exception);
};
// 非UI线程异常
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
if (e.ExceptionObject is Exception ex)
{
Logger.Fatal(ex, "非UI线程未处理异常");
}
};
// Task异常
TaskScheduler.UnobservedTaskException += (s, e) =>
{
Logger.Error(e.Exception, "Task未观察异常");
e.SetObserved();
};
}
private void ShowErrorDialog(string title, Exception ex)
{
// 使用ModernWPF或自定义对话框
MessageBox.Show($"{title}\n\n{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
public static void ConfigureServices(IServiceCollection services)
{
// 这个方法可以用于外部配置
ConfigureServices(null!, services);
}
}
}