diff --git a/UIShare/GlobalVariable/DeviceManager.cs b/UIShare/GlobalVariable/DeviceManager.cs index 2a9fa7e..07d753e 100644 --- a/UIShare/GlobalVariable/DeviceManager.cs +++ b/UIShare/GlobalVariable/DeviceManager.cs @@ -15,11 +15,12 @@ namespace UIShare.GlobalVariable /// 设备管理器:根据 反射实例化所有启用的设备, /// 通过 多态统一管理,避免为每种设备单独硬编码字段。 /// - public class DeviceManager + public class DeviceManager:IDisposable { private object _lockObj = new object(); public SystemConfig _systemConfig { get; set; } private readonly GlobalInfo _globalInfo; + private readonly string _scopeName; /// 按 DeviceName 索引的设备字典,便于业务层按名取实例。 public IDictionary DeviceMap { get; private set; } @@ -32,6 +33,8 @@ namespace UIShare.GlobalVariable { _systemConfig = systemConfig; _globalInfo = globalInfo; + // 用 SystemConfig.Title 作为作用域唯一标识,无需反查 ConfigDic + _scopeName = _systemConfig.Title; InitDevices(); } /// @@ -108,6 +111,18 @@ namespace UIShare.GlobalVariable DeviceMap[config.DeviceName] = instance; } + // 第五步:将当前作用域注册到指纹的作用域列表,用于引用计数与安全销毁 + if (!string.IsNullOrEmpty(_scopeName)) + { + var scopeList = _globalInfo.DeviceAndScopeDic.GetOrAdd(fingerprint, + _ => new Lazy>(() => new List())).Value; + lock (scopeList) + { + if (!scopeList.Contains(_scopeName)) + scopeList.Add(_scopeName); + } + } + LoggerHelper.Info($"已加载设备 [{config.DeviceName} / {config.DeviceType} / {config.ConnectionType}] 指纹={fingerprint}"); } catch (Exception ex) @@ -338,6 +353,50 @@ namespace UIShare.GlobalVariable }; return Activator.CreateInstance(type, cfg) as IBaseInterface; } + + public void Dispose() + { + if (string.IsNullOrEmpty(_scopeName)) return; + + // 遍历当前作用域用到的所有指纹,逐一移除本作用域的引用 + var fingerprintsToRemove = new List(); + + foreach (var kvp in _globalInfo.DeviceAndScopeDic) + { + string fingerprint = kvp.Key; + var scopeList = kvp.Value.IsValueCreated ? kvp.Value.Value : null; + if (scopeList == null) continue; + + lock (scopeList) + { + scopeList.Remove(_scopeName); + + // 引用归零 → 标记为待清理 + if (scopeList.Count == 0) + fingerprintsToRemove.Add(fingerprint); + } + } + + // 对引用归零的指纹:销毁设备实例 + 从全局池中移除 + foreach (var fingerprint in fingerprintsToRemove) + { + // 尝试从 HardwarePool 取出并销毁 + if (_globalInfo.HardwarePool.TryRemove(fingerprint, out var lazy)) + { + if (lazy.IsValueCreated && lazy.Value is IBaseInterface device) + { + try { device.Close(); } + catch { /* 销毁时忽略异常 */ } + LoggerHelper.Info($"指纹 [{fingerprint}] 无作用域引用,已销毁设备实例。"); + } + } + + // 同步清除 DeviceAndScopeDic 中的空条目 + _globalInfo.DeviceAndScopeDic.TryRemove(fingerprint, out _); + } + + DeviceMap.Clear(); + } #endregion } } diff --git a/UIShare/GlobalVariable/GlobalInfo.cs b/UIShare/GlobalVariable/GlobalInfo.cs index fde936c..2e7f0f1 100644 --- a/UIShare/GlobalVariable/GlobalInfo.cs +++ b/UIShare/GlobalVariable/GlobalInfo.cs @@ -19,6 +19,9 @@ namespace UIShare.GlobalVariable /// 硬件指纹 → 设备实例的并发池,确保同一物理硬件全局只创建一个驱动实例。 public ConcurrentDictionary> HardwarePool { get; set; } + /// 硬件指纹 → 正在使用该设备的作用域名称列表,用于引用计数与安全销毁。 + public ConcurrentDictionary>> DeviceAndScopeDic { get; set; } + public String UserName { get; set; } = "Not Logged in"; public bool IsAdmin { get; set; } = true; public string CurrentOpeningScope; @@ -42,6 +45,7 @@ namespace UIShare.GlobalVariable ConfigDic = new(); ScopeDic = new(); HardwarePool = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + DeviceAndScopeDic = new ConcurrentDictionary>>(StringComparer.OrdinalIgnoreCase); CurrentScope = "default"; }