using System; using System.Configuration; using System.Reflection; using System.Text; using System.Threading; using XP.Common.License.Configs; using XP.Common.License.Enums; using XP.Common.License.Interfaces; using XP.Common.License.Native; using XP.Common.Logging.Interfaces; namespace XP.Common.License.Implementations { /// /// 授权服务实现 | License service implementation /// public class LicenseService : ILicenseService, IDisposable { /// /// 临时测试模式初始时间(秒)| Temporary test mode initial time (seconds) /// private const int TestModeInitialSeconds = 900; /// /// 授权配置 | License configuration /// private readonly LicenseConfig _config; /// /// 日志服务 | Logger service /// private readonly ILoggerService _logger; /// /// 同步锁对象 | Synchronization lock object /// private readonly object _lock = new object(); /// /// 临时测试模式计时器 | Temporary test mode timer /// private Timer? _testModeTimer; /// /// 临时测试模式剩余时间(秒)| Temporary test mode remaining time (seconds) /// private int _remainingTestSeconds = TestModeInitialSeconds; /// /// 授权到期日期 | License expiration date /// private DateTime? _expirationDate; /// /// SMA到期日期 | SMA expiration date /// private DateTime? _smaDate; /// /// 浮动许可IP地址 | Floating license IP address /// private string? _floatingLicenseIp; /// /// 浮动许可端口 | Floating license port /// private string? _floatingLicensePort; /// /// 是否已释放 | Whether disposed /// private bool _disposed; /// /// 构造函数 | Constructor /// /// 授权配置 | License configuration /// 日志服务 | Logger service public LicenseService(LicenseConfig config, ILoggerService logger) { _config = config ?? throw new ArgumentNullException(nameof(config)); _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); LicenseMode = (LicenseMode)_config.LicenseMode; } /// /// 当前会话是否已授权 | Whether the current session is authorized /// public bool IsAuthorized { get; private set; } /// /// 当前授权模式 | Current license mode /// public LicenseMode LicenseMode { get; private set; } /// /// 临时测试模式超时事件(到期时触发)| Temporary test mode timeout event (fires when expired) /// public event EventHandler? TestModeTimeout; /// /// 临时测试模式剩余5分钟警告事件 | Temporary test mode 5-minute warning event /// public event EventHandler? TestModeWarning5Min; /// /// 临时测试模式剩余1分钟警告事件 | Temporary test mode 1-minute warning event /// public event EventHandler? TestModeWarning1Min; /// /// 是否已触发5分钟警告 | Whether 5-minute warning has been fired /// private bool _warning5MinFired; /// /// 是否已触发1分钟警告 | Whether 1-minute warning has been fired /// private bool _warning1MinFired; /// /// 执行授权检查 | Perform authorization check /// /// 授权检查结果 | License check result public LicenseCheckResult CheckAuthorization() { lock (_lock) { _logger.Info("开始授权检查,模式={Mode} | Starting authorization check, mode={Mode}", (int)LicenseMode); if (LicenseMode == Enums.LicenseMode.TemporaryTest) { return HandleTemporaryTestMode(); } return HandleClmsAuthorization(); } } /// /// 获取授权到期日期 | Get license expiration date /// /// 授权到期日期,未授权时返回 null | License expiration date, null if not authorized public DateTime? GetExpirationDate() { lock (_lock) { return _expirationDate; } } /// /// 检查模块是否授权 | Check if module is licensed /// /// 模块ID | Module ID /// 模块是否授权 | Whether the module is licensed public bool IsModuleLicensed(ushort moduleId) { lock (_lock) { if (!IsAuthorized) return false; try { ushort mod = moduleId; ushort type = 0; return NativeMethods.CLM_ModuleIsLicensed(ref mod, ref type); } catch (DllNotFoundException ex) { _logger.Error(ex, "MORCODE.dll 加载失败 | Failed to load MORCODE.dll"); return false; } catch (EntryPointNotFoundException ex) { _logger.Error(ex, "MORCODE.dll 中缺少入口点 | Missing entry point in MORCODE.dll"); return false; } } } /// /// 获取SMA到期日期 | Get SMA expiration date /// /// SMA到期日期,未启用时返回 null | SMA expiration date, null if not enabled public DateTime? GetSmaDate() { lock (_lock) { return _smaDate; } } /// /// 获取临时测试模式剩余时间 | Get remaining time in temporary test mode /// /// 剩余时间(秒),非测试模式返回 -1 | Remaining time in seconds, -1 if not in test mode public int GetRemainingTestTime() { if (LicenseMode != Enums.LicenseMode.TemporaryTest) return -1; return Interlocked.CompareExchange(ref _remainingTestSeconds, 0, 0); } /// /// 释放资源 | Release resources /// public void Dispose() { if (_disposed) return; _disposed = true; // 停止计时器 | Stop timer _testModeTimer?.Dispose(); _testModeTimer = null; // 尝试登出 | Try to logout try { NativeMethods.CLM_Logout(); } catch (Exception ex) { _logger.Error(ex, "CLM_Logout 调用失败 | CLM_Logout call failed"); } } /// /// 处理临时测试模式 | Handle temporary test mode /// /// 授权检查结果 | License check result private LicenseCheckResult HandleTemporaryTestMode() { IsAuthorized = true; _remainingTestSeconds = TestModeInitialSeconds; // 启动计时器,每秒递减 | Start timer, decrement every second _testModeTimer?.Dispose(); _testModeTimer = new Timer(TestModeTimerCallback, null, 1000, 1000); _logger.Info("临时测试模式已启动,剩余时间={Seconds}秒 | Temporary test mode started, remaining time={Seconds}s", TestModeInitialSeconds); WriteLicenseStateToConfig(LicenseState.Success); return new LicenseCheckResult( isAuthorized: true, message: "临时测试模式已启动,有效时间15分钟 | Temporary test mode started, valid for 15 minutes", licenseMode: Enums.LicenseMode.TemporaryTest, moduleId: _config.ModuleId, expirationDate: null, smaDate: null, floatingLicenseIp: null, floatingLicensePort: null); } /// /// 临时测试模式计时器回调 | Temporary test mode timer callback /// /// 状态对象 | State object private void TestModeTimerCallback(object? state) { int remaining = Interlocked.Decrement(ref _remainingTestSeconds); // 剩余5分钟(300秒)时触发警告 | Fire warning at 5 minutes (300 seconds) remaining if (remaining <= 300 && !_warning5MinFired) { _warning5MinFired = true; _logger.Warn("临时测试模式剩余5分钟 | Temporary test mode: 5 minutes remaining"); TestModeWarning5Min?.Invoke(this, EventArgs.Empty); } // 剩余1分钟(60秒)时触发警告 | Fire warning at 1 minute (60 seconds) remaining if (remaining <= 60 && !_warning1MinFired) { _warning1MinFired = true; _logger.Warn("临时测试模式剩余1分钟 | Temporary test mode: 1 minute remaining"); TestModeWarning1Min?.Invoke(this, EventArgs.Empty); } // 到期时触发超时事件 | Fire timeout event when expired if (remaining <= 0) { // 停止计时器 | Stop timer _testModeTimer?.Dispose(); _testModeTimer = null; _logger.Info("临时测试模式已超时 | Temporary test mode has timed out"); // 触发超时事件 | Raise timeout event TestModeTimeout?.Invoke(this, EventArgs.Empty); } } /// /// 处理 CLMS 正式授权流程 | Handle CLMS formal authorization flow /// 兼容新旧版本 SDK:对可能不存在的入口点使用 TryInvoke 优雅降级 | /// Compatible with old/new SDK: gracefully degrades for missing entry points via TryInvoke /// /// 授权检查结果 | License check result private LicenseCheckResult HandleClmsAuthorization() { try { // 步骤 1:检查系统时间(可选,老版本 SDK 可能不支持)| Step 1: Check system time (optional, old SDK may not support) if (!TryInvokeOptional(() => NativeMethods.CLM_CheckSystemTime(), "CLM_CheckSystemTime", out bool checkTimeResult)) { // 入口点不存在,跳过此步骤 | Entry point not found, skip this step _logger.Warn("CLM_CheckSystemTime 入口点不存在,跳过系统时间检查(SDK版本较旧)| CLM_CheckSystemTime entry point not found, skipping system time check (older SDK version)"); } else if (!checkTimeResult) { _logger.Error(new InvalidOperationException("CLM_CheckSystemTime"), "系统时间检查异常 | System time check anomaly"); return CreateFailureResult("系统时间检查异常 | System time check anomaly"); } else { _logger.Info("系统时间检查正常 | System time check: OK"); } // 步骤 2:登录验证(核心,必须存在)| Step 2: Login verification (core, must exist) StringBuilder password = new StringBuilder("FnEoFWSNLpVeoNWYhVoHLfgITRvieSszJfylVsXOTsLkphgkPzPhbLQzQrvRbNOkVVIQyMWkyGVjWSaiYUEksfQsRmklksLxrmeTksKKNMoZoWfZeDaLDSyWwEmtQakvSNxBMBLHoLEZHtaoXNpTWiaUGaSLQdsHFZnbRyPehytarNTKpaNNqnjFNggqWifhFsrZasDsWbIGWDrhnGrdtUNDMjJdhlTunsssxCzYpsLQrWBxUkuUUEJraSbTlbuX"); if (!NativeMethods.CLM_Login(password)) { _logger.Error(new InvalidOperationException("CLM_Login"), "CLM_Login 登录验证失败 | CLM_Login verification failed"); return CreateFailureResult("CLM_Login 登录验证失败 | CLM_Login verification failed"); } _logger.Info("CLM_Login 登录验证成功 | CLM_Login verification: OK"); // 步骤 3:检查许可范围(核心,必须存在)| Step 3: Check license scope (core, must exist) if (!NativeMethods.CLM_Login_Scope()) { _logger.Error(new InvalidOperationException("CLM_Login_Scope"), "CLM_Login_Scope 许可范围检查失败 | CLM_Login_Scope scope check failed"); return CreateFailureResult("CLM_Login_Scope 许可范围检查失败 | CLM_Login_Scope scope check failed"); } _logger.Info("CLM_Login_Scope 许可范围检查成功 | CLM_Login_Scope scope check: OK"); // 步骤 4:SMA 验证(如果启用,可选入口点)| Step 4: SMA validation (if enabled, optional entry point) if (_config.UseSma) { var smaResult = ValidateSma(); if (smaResult != null) return smaResult; } else { _logger.Info("放弃检查SMA | SMA check skipped"); } // 步骤 5:获取浮动许可IP和端口(可选,老版本 SDK 可能不支持)| Step 5: Get floating license IP and port (optional, old SDK may not support) StringBuilder ip = new StringBuilder(256); StringBuilder port = new StringBuilder(256); if (!TryInvokeOptional(() => NativeMethods.CLM_GetIP(ip, port), "CLM_GetIP", out bool getIpResult)) { _logger.Warn("CLM_GetIP 入口点不存在,跳过浮动许可IP获取(SDK版本较旧)| CLM_GetIP entry point not found, skipping floating license IP retrieval (older SDK version)"); } else if (!getIpResult) { _logger.Error(new InvalidOperationException("CLM_GetIP"), "CLM_GetIP 获取浮动许可IP失败 | CLM_GetIP floating license IP retrieval failed"); return CreateFailureResult("CLM_GetIP 获取浮动许可IP失败 | CLM_GetIP floating license IP retrieval failed"); } else { _floatingLicenseIp = ip.ToString(); _floatingLicensePort = port.ToString(); _logger.Info("CLM_GetIP 成功: ip={Ip}/port={Port} | CLM_GetIP success: ip={Ip}/port={Port}", _floatingLicenseIp, _floatingLicensePort); } // 步骤 6:获取错误信息(可选,老版本 SDK 可能不支持)| Step 6: Get error message (optional, old SDK may not support) StringBuilder error = new StringBuilder(512); if (!TryInvokeOptional(() => NativeMethods.CLM_GetError(error), "CLM_GetError", out bool getErrorResult)) { _logger.Warn("CLM_GetError 入口点不存在,跳过错误信息获取(SDK版本较旧)| CLM_GetError entry point not found, skipping error retrieval (older SDK version)"); } else if (!getErrorResult) { _logger.Error(new InvalidOperationException("CLM_GetError"), "CLM_GetError 获取错误信息失败 | CLM_GetError error retrieval failed"); return CreateFailureResult("CLM_GetError 获取错误信息失败 | CLM_GetError error retrieval failed"); } else { _logger.Info("CLM_GetError 成功: {Error} | CLM_GetError success: {Error}", error.ToString()); } // 步骤 7:检查模块授权(核心,必须存在)| Step 7: Check module authorization (core, must exist) ushort moduleId = _config.ModuleId; ushort type = 0; if (!NativeMethods.CLM_ModuleIsLicensed(ref moduleId, ref type)) { _logger.Error(new InvalidOperationException("CLM_ModuleIsLicensed"), "模块号码{ModuleId}不可用 | Module {ModuleId} unavailable", moduleId); return CreateFailureResult($"模块号码{moduleId}不可用 | Module {moduleId} unavailable"); } _logger.Info("模块号码{ModuleId}有效 | Module {ModuleId} available", moduleId); // 步骤 8:获取授权到期日期(核心,必须存在)| Step 8: Get warranty expiration date (core, must exist) int month = 0, day = 0, year = 0; if (!NativeMethods.CLM_GetWarrantyExpiration(ref month, ref day, ref year)) { _logger.Error(new InvalidOperationException("CLM_GetWarrantyExpiration"), "获取授权到期日期失败 | Failed to get warranty expiration date"); return CreateFailureResult("获取授权到期日期失败 | Failed to get warranty expiration date"); } _expirationDate = new DateTime(year, month, day); _logger.Info("CLM_GetWarrantyExpiration 成功: {Year}/{Month}/{Day} | CLM_GetWarrantyExpiration success: {Year}/{Month}/{Day}", year, month, day); // 检查是否在30天内到期 | Check if expiring within 30 days string warningMessage = string.Empty; TimeSpan timeToExpiry = _expirationDate.Value - DateTime.Now; if (timeToExpiry.Days <= 30) { warningMessage = $"软件授权将于{year}年{month}月{day}日到期,请尽快联系海克斯康 | Software license will expire on {year}-{month}-{day}, please contact Hexagon"; _logger.Warn(warningMessage); } // 授权成功 | Authorization successful IsAuthorized = true; WriteLicenseStateToConfig(LicenseState.Success); string successMessage = string.IsNullOrEmpty(warningMessage) ? "授权检查成功 | Authorization check successful" : $"授权检查成功(警告:{warningMessage})| Authorization check successful (Warning: {warningMessage})"; _logger.Info("授权检查完成 | Authorization check completed: Mode={Mode}, State={State}, Expiration={Expiration}", (int)LicenseMode, (int)LicenseState.Success, _expirationDate?.ToString("yyyy-MM-dd") ?? "N/A"); return new LicenseCheckResult( isAuthorized: true, message: successMessage, licenseMode: LicenseMode, moduleId: _config.ModuleId, expirationDate: _expirationDate, smaDate: _smaDate, floatingLicenseIp: _floatingLicenseIp, floatingLicensePort: _floatingLicensePort); } catch (DllNotFoundException ex) { _logger.Error(ex, "MORCODE.dll 加载失败 | Failed to load MORCODE.dll"); return CreateFailureResult("CLMS SDK 不可用 | CLMS SDK unavailable"); } } /// /// 尝试调用可选的 SDK 方法,兼容老版本 SDK 中不存在的入口点 | /// Try to invoke an optional SDK method, compatible with missing entry points in older SDK versions /// /// 要调用的方法委托 | Method delegate to invoke /// 方法名称(用于日志)| Method name (for logging) /// 方法返回值,入口点不存在时为 default | Method return value, default if entry point not found /// true: 入口点存在并已调用; false: 入口点不存在(EntryPointNotFoundException)| true: entry point exists and was invoked; false: entry point not found private bool TryInvokeOptional(Func action, string methodName, out bool result) { try { result = action(); return true; } catch (EntryPointNotFoundException) { result = default; return false; } } /// /// 验证 SMA | Validate SMA /// 兼容老版本 SDK:CLM_GetSmaDate 入口点不存在时跳过 SMA 验证 | /// Compatible with old SDK: skips SMA validation if CLM_GetSmaDate entry point not found /// /// 失败时返回失败结果,成功或跳过时返回 null | Returns failure result on failure, null on success or skip private LicenseCheckResult? ValidateSma() { int yearSma = 0, monthSma = 0, daySma = 0; // CLM_GetSmaDate 在老版本 SDK 中可能不存在 | CLM_GetSmaDate may not exist in older SDK try { if (!NativeMethods.CLM_GetSmaDate(ref monthSma, ref daySma, ref yearSma)) { _logger.Error(new InvalidOperationException("CLM_GetSmaDate"), "检查SMA失败 | SMA check failed"); return CreateFailureResult("检查SMA失败 | SMA check failed"); } } catch (EntryPointNotFoundException) { _logger.Warn("CLM_GetSmaDate 入口点不存在,跳过SMA验证(SDK版本较旧)| CLM_GetSmaDate entry point not found, skipping SMA validation (older SDK version)"); return null; } _logger.Info("CLM_GetSmaDate 成功: {Year}/{Month}/{Day} | CLM_GetSmaDate success: {Year}/{Month}/{Day}", yearSma, monthSma, daySma); // 获取软件版本信息 | Get software version information var version = Assembly.GetExecutingAssembly().GetName().Version; int major = version?.Major ?? 0; int minor = version?.Minor ?? 0; // SMA 年份 < 软件主版本号 → 失败 | SMA year < software major version → failure if (yearSma < major) { string msg = $"CLMS授权中SMA年份{yearSma}小于软件主版本号(年份){major},请联系海克斯康升级许可 | SMA year {yearSma} is less than software major version {major}, please contact Hexagon to upgrade license"; _logger.Error(new InvalidOperationException("SMA验证失败"), msg); return CreateFailureResult(msg); } // SMA 年份 == 软件主版本号时,校验季度 | When SMA year == software major version, validate quarter if (yearSma == major) { try { DateTime smaDate = new DateTime(yearSma, monthSma, daySma); int smaQuarter = (smaDate.Month - 1) / 3 + 1; if (minor > smaQuarter) { string msg = $"CLMS授权日期{yearSma}/{monthSma}/{daySma}属于{yearSma}年第{smaQuarter}季度,不支持当前{major}年第{minor}季度的软件版本 | SMA date {yearSma}/{monthSma}/{daySma} is in Q{smaQuarter} of {yearSma}, does not support current Q{minor} of {major} software version"; _logger.Error(new InvalidOperationException("SMA季度验证失败"), msg); return CreateFailureResult(msg); } } catch (Exception ex) { string msg = $"SMA授权日期{yearSma}/{monthSma}/{daySma}不合法 | SMA date {yearSma}/{monthSma}/{daySma} is invalid: {ex.Message}"; _logger.Error(ex, msg); return CreateFailureResult(msg); } } _smaDate = new DateTime(yearSma, monthSma, daySma); _logger.Info("SMA校验成功,SMA有效期至{Year}/{Month}/{Day} | SMA validation successful, valid until {Year}/{Month}/{Day}", yearSma, monthSma, daySma); return null; } /// /// 创建失败结果 | Create failure result /// /// 失败消息 | Failure message /// 授权检查失败结果 | License check failure result private LicenseCheckResult CreateFailureResult(string message) { IsAuthorized = false; WriteLicenseStateToConfig(LicenseState.Fail); _logger.Info("授权检查完成 | Authorization check completed: Mode={Mode}, State={State}, Expiration={Expiration}", (int)LicenseMode, (int)LicenseState.Fail, "N/A"); return new LicenseCheckResult( isAuthorized: false, message: message, licenseMode: LicenseMode, moduleId: _config.ModuleId, expirationDate: null, smaDate: _smaDate, floatingLicenseIp: _floatingLicenseIp, floatingLicensePort: _floatingLicensePort); } /// /// 写入授权状态到配置文件 | Write license state to configuration file /// /// 授权状态 | License state private void WriteLicenseStateToConfig(LicenseState state) { try { var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); var settings = config.AppSettings.Settings; if (settings["License:LicenseState"] == null) { settings.Add("License:LicenseState", ((int)state).ToString()); } else { settings["License:LicenseState"].Value = ((int)state).ToString(); } config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection("appSettings"); _logger.Info("授权状态已写入配置: {State} | License state written to config: {State}", (int)state); } catch (Exception ex) { _logger.Error(ex, "写入授权状态到配置失败 | Failed to write license state to config"); } } } }