using System.IO; using System.Reflection; namespace DeviceService.Driver { /// /// 驱动的程序集描述(跨 AppDomain 按值封送)。 /// 注意:这些 DTO 只服务于"宿主 <-> 加载器"之间的通信, /// 驱动 dll 不需要引用本程序集,也不需要实现任何接口。 /// [Serializable] public sealed class DriverAssemblyInfo { /// dll 文件名 public string FileName { get; set; } /// 程序集完整标识(加载成功时) public string AssemblyFullName { get; set; } /// 加载失败原因(成功时为 null) public string Error { get; set; } /// 带 [DriverDesc] 注解的驱动类型(按类型名排序) public DriverTypeInfo[] Types { get; set; } } /// /// 一个驱动类型的描述信息(来自 DriverDescAttribute)。 /// 通过 (类型, 品牌) 可唯一定位到该类型。 /// [Serializable] public sealed class DriverTypeInfo { /// CLR 类型全名 public string FullName { get; set; } /// 注解中的"类型"(如 CAN、串口) public string DriverType { get; set; } /// 注解中的"品牌"(如 BA、BB) public string Brand { get; set; } } /// /// 驱动类型上的一个公共方法。 /// [Serializable] public sealed class DriverMethodInfo { /// 方法在该类型方法表中的索引(调用时回传) public int Index { get; set; } public string Name { get; set; } /// 可读签名,例如 int ReadStatus(int channel) public string Signature { get; set; } public bool IsStatic { get; set; } public int ParameterCount { get; set; } /// 参数列表(类型名 + 参数名),用于界面提示 public string[] Parameters { get; set; } } /// /// 反射调用结果。 /// [Serializable] public sealed class InvokeResult { public bool Success { get; set; } /// 返回值文本(成功时) public string Output { get; set; } /// 错误信息(失败时) public string Error { get; set; } } /// /// 直接调用结果:保留原始返回值与 out/ref 回填值, /// 供 RealProxy 动态代理把驱动调用适配成强类型接口方法。 /// 注意:Value / ArgsOut 只能携带可跨域封送的数据(基元类型、string、[Serializable] DTO)。 /// [Serializable] public sealed class InvokeDirectResult { public bool Success { get; set; } /// 方法返回值(void 方法为 null) public object Value { get; set; } /// 调用后的参数数组(out/ref 位置为回填值,其余位置原值) public object[] ArgsOut { get; set; } /// 错误信息(失败时) public string Error { get; set; } } /// /// 运行在每个驱动包专属 AppDomain 内的加载器。 /// /// 设计前提:驱动 dll 来自不同厂商,彼此没有任何共同接口/基类约定, /// 甚至互不兼容(同一第三方依赖的不同版本)。 /// 因此这里只做通用反射:枚举公开类型、枚举公共方法、反射创建实例、反射调用。 /// /// 该域的 ApplicationBase 被设置为某个驱动子文件夹, /// 所以驱动的全部依赖只能从自己目录解析,天然隔离版本冲突。 /// public class DriverLoader : MarshalByRefObject { private static readonly HashSet FailedAssemblies = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _types = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _methodCache = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _instances = new Dictionary(); /// 由 DriverManager 创建、按句柄管理的驱动实例 private readonly Dictionary _managedInstances = new Dictionary(StringComparer.Ordinal); /// 驱动索引:(类型, 品牌) → 驱动信息,key 形如 "CAN|BA" private readonly Dictionary _driverIndex = new Dictionary(StringComparer.OrdinalIgnoreCase); // 无限生存期,避免远程代理被回收 public override object InitializeLifetimeService() => null; public DriverLoader() { AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve; } #region 发现 /// /// 扫描本驱动目录下的所有 dll(忽略宿主自身的 MultiPlugin.* 运行时文件), /// 返回每个程序集的信息与公开类型列表。 /// public DriverAssemblyInfo[] LoadAssemblies() { var dir = AppDomain.CurrentDomain.BaseDirectory; var result = new List(); foreach (var path in Directory.GetFiles(dir, "*.dll").OrderBy(p => p, StringComparer.OrdinalIgnoreCase)) { var fileName = Path.GetFileName(path); if (fileName.StartsWith("MultiPlugin.", StringComparison.OrdinalIgnoreCase)) { continue; // 宿主基础设施,非驱动 } var info = new DriverAssemblyInfo { FileName = fileName }; try { var assembly = Assembly.Load(Path.GetFileNameWithoutExtension(path)); info.AssemblyFullName = assembly.FullName; // 只收录带 [DriverDesc] 注解的类,其余类型一律忽略 var drivers = new List(); foreach (var type in assembly.GetExportedTypes()) { if (!type.IsClass || type.IsAbstract) { continue; } var desc = ReadDriverDesc(type); if (desc == null) { continue; } _types[type.FullName] = type; var driverInfo = new DriverTypeInfo { FullName = type.FullName, DriverType = desc.Item1, Brand = desc.Item2 }; drivers.Add(driverInfo); _driverIndex[DriverKey(desc.Item1, desc.Item2)] = driverInfo; } info.Types = drivers.OrderBy(t => t.FullName, StringComparer.Ordinal).ToArray(); } catch (Exception ex) { // 非托管 dll、缺少依赖、架构不匹配等都会到这里 info.Error = ex.Message; } result.Add(info); } return result.ToArray(); } /// /// 按 (类型, 品牌) 查找驱动。任一字段留空视为通配。 /// /// 命中的第一个驱动信息;未命中返回 null public DriverTypeInfo FindDriver(string driverType, string brand) { var key = DriverKey(driverType, brand); if (_driverIndex.TryGetValue(key, out var exact)) { return exact; } // 精确未命中且存在通配字段时线性扫描 var anyType = key.StartsWith("*|", StringComparison.Ordinal); var anyBrand = key.EndsWith("|*", StringComparison.Ordinal); if (!anyType && !anyBrand) { return null; } foreach (var info in _driverIndex.Values) { if (!anyType && !string.Equals(info.DriverType?.Trim(), driverType?.Trim(), StringComparison.OrdinalIgnoreCase)) { continue; } if (!anyBrand && !string.Equals(info.Brand?.Trim(), brand?.Trim(), StringComparison.OrdinalIgnoreCase)) { continue; } return info; } return null; } /// /// 用鸭子反射读取 [DriverDesc] 注解的 Type/Brand 属性值。 /// 宿主基础设施不引用 Driver.Core,只按"属性名"解耦读取, /// 这样厂商 SDK 升级/换版本不会影响宿主。 /// private static Tuple ReadDriverDesc(Type type) { foreach (var attr in type.GetCustomAttributes(false)) { var attrType = attr.GetType(); if (!attrType.Name.Equals("DriverDescAttribute", StringComparison.OrdinalIgnoreCase)) { continue; } var typeProp = attrType.GetProperty("Type"); var brandProp = attrType.GetProperty("Brand"); if (typeProp == null || brandProp == null) { continue; } return Tuple.Create(typeProp.GetValue(attr, null) as string, brandProp.GetValue(attr, null) as string); } return null; } private static string DriverKey(string driverType, string brand) { var t = string.IsNullOrWhiteSpace(driverType) ? "*" : driverType.Trim().ToUpperInvariant(); var b = string.IsNullOrWhiteSpace(brand) ? "*" : brand.Trim().ToUpperInvariant(); return $"{t}|{b}"; } /// /// 列出某个类型的公共方法(属性访问器会被过滤掉)。 /// public DriverMethodInfo[] GetMethods(string typeFullName) { var type = ResolveType(typeFullName); if (type == null) { return new DriverMethodInfo[0]; } return GetMethodInfos(type) .Select((m, i) => { var parameters = m.GetParameters(); return new DriverMethodInfo { Index = i, Name = m.Name, IsStatic = m.IsStatic, ParameterCount = parameters.Length, Signature = BuildSignature(m), Parameters = parameters .Select(p => $"{FormatTypeName(p.ParameterType)} {p.Name}") .ToArray() }; }) .ToArray(); } #endregion #region 实例管理 private sealed class ManagedInstance { public Type Type; public object Target; } /// /// 创建驱动类型的新实例(不走 Invoke 的默认缓存实例),按句柄登记供 DriverManager 管理。 /// public DriverInstance CreateInstance(string typeFullName, string driverType, string brand) { var type = ResolveType(typeFullName) ?? throw new InvalidOperationException($"未找到类型: {typeFullName}"); var ctor = type.GetConstructor(Type.EmptyTypes) ?? throw new MissingMethodException(typeFullName, ".ctor(缺少公共无参构造函数)"); var target = ctor.Invoke(null); var handle = Guid.NewGuid().ToString("N"); _managedInstances[handle] = new ManagedInstance { Type = type, Target = target }; return new DriverInstance(this, handle, type.FullName, driverType, brand); } /// /// 在指定句柄的驱动实例上按"原始对象参数"直接调用方法, /// 保留原始返回值与 out/ref 回填值(供 RealProxy 动态代理使用)。 /// public InvokeDirectResult InvokeOnInstanceDirect(string handle, int methodIndex, object[] args) { var result = new InvokeDirectResult(); if (!_managedInstances.TryGetValue(handle, out var managed)) { result.Success = false; result.Error = $"实例不存在或已释放: {handle}"; return result; } var methods = GetMethodInfos(managed.Type); if (methodIndex < 0 || methodIndex >= methods.Length) { result.Success = false; result.Error = $"方法索引无效: {methodIndex}(共 {methods.Length} 个方法)"; return result; } try { var method = methods[methodIndex]; var parameters = method.GetParameters(); args = args ?? new object[0]; if (parameters.Length != args.Length) { result.Success = false; result.Error = $"参数数量不匹配: {method.Name} 需要 {parameters.Length} 个,实际传入 {args.Length} 个"; return result; } var converted = new object[parameters.Length]; for (var i = 0; i < parameters.Length; i++) { converted[i] = CoerceArg(args[i], parameters[i].ParameterType, parameters[i].Name); } result.Value = method.Invoke(managed.Target, converted); result.ArgsOut = converted; result.Success = true; return result; } catch (TargetInvocationException ex) { var inner = ex.InnerException ?? ex; result.Success = false; result.Error = $"{inner.GetType().Name}: {inner.Message}"; return result; } catch (Exception ex) { result.Success = false; result.Error = $"{ex.GetType().Name}: {ex.Message}"; return result; } } /// /// 在指定句柄的驱动实例上反射调用方法(实际执行发生在驱动自己的域内)。 /// public InvokeResult InvokeOnInstance(string handle, int methodIndex, string[] rawArgs) { if (!_managedInstances.TryGetValue(handle, out var managed)) { return Fail($"实例不存在或已释放: {handle}"); } var methods = GetMethodInfos(managed.Type); if (methodIndex < 0 || methodIndex >= methods.Length) { return Fail($"方法索引无效: {methodIndex}(共 {methods.Length} 个方法)"); } return ExecuteMethod(methods[methodIndex], managed.Target, rawArgs); } /// /// 释放指定句柄的实例;驱动实现 IDisposable 时会调用 Dispose。 /// public void ReleaseInstance(string handle) { if (!_managedInstances.TryGetValue(handle, out var managed)) { return; } _managedInstances.Remove(handle); (managed.Target as IDisposable)?.Dispose(); } #endregion #region 调用 /// /// 反射调用指定类型的方法。 /// 实例方法会自动用公共无参构造函数创建(并缓存)实例; /// 静态方法无需实例。需要长期持有实例请使用 。 /// /// 字符串形式的实参,由 按目标参数类型转换 public InvokeResult Invoke(string typeFullName, int methodIndex, string[] rawArgs) { try { var type = ResolveType(typeFullName); if (type == null) { return Fail($"未找到类型: {typeFullName}"); } var methods = GetMethodInfos(type); if (methodIndex < 0 || methodIndex >= methods.Length) { return Fail($"方法索引无效: {methodIndex}(共 {methods.Length} 个方法)"); } var method = methods[methodIndex]; object target = null; if (!method.IsStatic) { target = GetOrCreateInstance(type); if (target == null) { return Fail($"无法实例化 {type.FullName}: 缺少公共无参构造函数。可改为只调用该类型的静态方法。"); } } return ExecuteMethod(method, target, rawArgs); } catch (TargetInvocationException ex) { // 驱动内部抛出的异常 var inner = ex.InnerException ?? ex; return Fail($"{inner.GetType().Name}: {inner.Message}"); } catch (Exception ex) { return Fail($"{ex.GetType().Name}: {ex.Message}"); } } /// /// 参数转换 → 反射调用 → 汇总返回值与 out/ref 回填值。 /// private static InvokeResult ExecuteMethod(MethodInfo method, object target, string[] rawArgs) { try { var parameters = method.GetParameters(); rawArgs = rawArgs ?? new string[0]; if (parameters.Length != rawArgs.Length) { return Fail($"参数数量不匹配: {method.Name} 需要 {parameters.Length} 个参数,实际传入 {rawArgs.Length} 个"); } var args = new object[parameters.Length]; for (var i = 0; i < parameters.Length; i++) { args[i] = ConvertArg(rawArgs[i], parameters[i].ParameterType, parameters[i].Name); } var returnValue = method.Invoke(target, args); // 汇总返回值与 out/ref 回填值(如 Open(out string msg) 的 msg) var parts = new List(); if (method.ReturnType != typeof(void)) { parts.Add($"返回: {Format(returnValue)}"); } for (var i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType.IsByRef) { parts.Add($"{parameters[i].Name} = {Format(args[i])}"); } } return new InvokeResult { Success = true, Output = parts.Count == 0 ? "null" : string.Join("; ", parts) }; } catch (TargetInvocationException ex) { // 驱动内部抛出的异常 var inner = ex.InnerException ?? ex; return Fail($"{inner.GetType().Name}: {inner.Message}"); } catch (Exception ex) { return Fail($"{ex.GetType().Name}: {ex.Message}"); } } private object GetOrCreateInstance(Type type) { if (_instances.TryGetValue(type, out var existing)) { return existing; } if (type.IsAbstract || type.IsInterface) { return null; } var ctor = type.GetConstructor(Type.EmptyTypes); if (ctor == null) { return null; } var instance = ctor.Invoke(null); _instances[type] = instance; return instance; } #endregion #region 辅助 private Type ResolveType(string typeFullName) { if (_types.TryGetValue(typeFullName, out var type)) { return type; } // 兜底:在已加载的驱动程序集里查找 type = AppDomain.CurrentDomain.GetAssemblies() .Where(a => !a.IsDynamic) .Select(a => a.GetType(typeFullName)) .FirstOrDefault(t => t != null); if (type != null) { _types[typeFullName] = type; } return type; } private MethodInfo[] GetMethodInfos(Type type) { if (_methodCache.TryGetValue(type.FullName, out var cached)) { return cached; } var methods = type .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly) .Where(m => !m.IsSpecialName) // 过滤 get_/set_/add_/remove_ 等 .ToArray(); _methodCache[type.FullName] = methods; return methods; } private static string BuildSignature(MethodInfo method) { var parameters = method.GetParameters() .Select(p => $"{FormatTypeName(p.ParameterType)} {p.Name}"); return $"{(method.IsStatic ? "static " : string.Empty)}{FormatTypeName(method.ReturnType)} " + $"{method.Name}({string.Join(", ", parameters)})"; } private static string FormatTypeName(Type type) { if (!type.IsGenericType) { return type.Name; } var name = type.Name.Substring(0, type.Name.IndexOf('`')); var args = string.Join(", ", type.GetGenericArguments().Select(FormatTypeName)); return $"{name}<{args}>"; } /// /// 把跨域代理传来的对象参数适配为目标参数类型: /// 类型兼容直接用,否则退化为字符串转换(鸭子式宽松匹配)。 /// private static object CoerceArg(object arg, Type targetType, string parameterName) { if (targetType.IsByRef) { targetType = targetType.GetElementType(); } if (arg == null) { return targetType.IsValueType ? Activator.CreateInstance(targetType) : null; } if (targetType.IsInstanceOfType(arg)) { return arg; } return ConvertArg(arg.ToString(), targetType, parameterName); } /// /// 把 UI 传来的字符串转换为目标参数类型。 /// 支持常用基元类型、枚举、Guid、DateTime、字符串数组(以分号分隔)。 /// private static object ConvertArg(string raw, Type targetType, string parameterName) { raw = raw ?? string.Empty; // out/ref 参数实际按其元素类型转换(调用后由 CLR 回填) if (targetType.IsByRef) { targetType = targetType.GetElementType(); } if (targetType == typeof(string) || targetType == typeof(object)) { return raw; } if (raw.Length == 0 && !targetType.IsValueType) { return null; } if (targetType == typeof(bool)) return bool.Parse(raw); if (targetType == typeof(char)) return raw[0]; if (targetType == typeof(byte)) return byte.Parse(raw); if (targetType == typeof(sbyte)) return sbyte.Parse(raw); if (targetType == typeof(short)) return short.Parse(raw); if (targetType == typeof(ushort)) return ushort.Parse(raw); if (targetType == typeof(int)) return int.Parse(raw); if (targetType == typeof(uint)) return uint.Parse(raw); if (targetType == typeof(long)) return long.Parse(raw); if (targetType == typeof(ulong)) return ulong.Parse(raw); if (targetType == typeof(float)) return float.Parse(raw); if (targetType == typeof(double)) return double.Parse(raw); if (targetType == typeof(decimal)) return decimal.Parse(raw); if (targetType == typeof(Guid)) return Guid.Parse(raw); if (targetType == typeof(DateTime)) return DateTime.Parse(raw); if (targetType.IsEnum) return Enum.Parse(targetType, raw, true); if (targetType.IsArray) { var elementType = targetType.GetElementType(); var parts = raw.Length == 0 ? new string[0] : raw.Split(';'); var array = Array.CreateInstance(elementType, parts.Length); for (var i = 0; i < parts.Length; i++) { array.SetValue(ConvertArg(parts[i], elementType, parameterName), i); } return array; } throw new InvalidOperationException( $"参数 {parameterName} 的类型 {targetType.Name} 暂不支持从字符串转换(支持:字符串、数字、bool、枚举、Guid、DateTime、数组)"); } private static string Format(object value) { if (value == null) { return "null"; } if (value is string text) { return Truncate(text); } if (value is Array array) { var items = array.Cast().Select(x => x == null ? "null" : x.ToString()); return Truncate($"[{string.Join(", ", items)}]"); } return Truncate(value.ToString()); } private static string Truncate(string text) { const int max = 4000; return text.Length <= max ? text : text.Substring(0, max) + " ...(已截断)"; } private static InvokeResult Fail(string message) => new InvokeResult { Success = false, Error = message }; /// /// 依赖解析兜底:只在本驱动目录(及加载器所在目录)内查找, /// 保证不会串到其他驱动包的同名程序集。 /// private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args) { var simpleName = new AssemblyName(args.Name).Name; if (FailedAssemblies.Contains(simpleName)) { return null; } var dirs = new List(); // 优先按加载器所在目录(宿主 bin)解析宿主基础设施,再兜底驱动目录 var loaderLocation = typeof(DriverLoader).Assembly.Location; var loaderDir = string.IsNullOrEmpty(loaderLocation) ? null : Path.GetDirectoryName(loaderLocation); if (!string.IsNullOrEmpty(loaderDir)) { dirs.Add(loaderDir); } var baseDir = AppDomain.CurrentDomain.BaseDirectory; if (!dirs.Contains(baseDir)) { dirs.Add(baseDir); } foreach (var dir in dirs) { foreach (var ext in new[] { ".dll", ".exe" }) { var candidate = Path.Combine(dir, simpleName + ext); if (!File.Exists(candidate)) { continue; } try { return Assembly.LoadFrom(candidate); } catch { // 尝试下一个候选 } } } FailedAssemblies.Add(simpleName); return null; } #endregion } }