using System.IO;
|
using System.Reflection;
|
|
namespace DeviceService.Driver
|
{
|
/// <summary>
|
/// 驱动的程序集描述(跨 AppDomain 按值封送)。
|
/// 注意:这些 DTO 只服务于"宿主 <-> 加载器"之间的通信,
|
/// 驱动 dll 不需要引用本程序集,也不需要实现任何接口。
|
/// </summary>
|
[Serializable]
|
public sealed class DriverAssemblyInfo
|
{
|
/// <summary>dll 文件名</summary>
|
public string FileName { get; set; }
|
|
/// <summary>程序集完整标识(加载成功时)</summary>
|
public string AssemblyFullName { get; set; }
|
|
/// <summary>加载失败原因(成功时为 null)</summary>
|
public string Error { get; set; }
|
|
/// <summary>带 [DriverDesc] 注解的驱动类型(按类型名排序)</summary>
|
public DriverTypeInfo[] Types { get; set; }
|
}
|
|
/// <summary>
|
/// 一个驱动类型的描述信息(来自 DriverDescAttribute)。
|
/// 通过 (类型, 品牌) 可唯一定位到该类型。
|
/// </summary>
|
[Serializable]
|
public sealed class DriverTypeInfo
|
{
|
/// <summary>CLR 类型全名</summary>
|
public string FullName { get; set; }
|
|
/// <summary>注解中的"类型"(如 CAN、串口)</summary>
|
public string DriverType { get; set; }
|
|
/// <summary>注解中的"品牌"(如 BA、BB)</summary>
|
public string Brand { get; set; }
|
}
|
|
/// <summary>
|
/// 驱动类型上的一个公共方法。
|
/// </summary>
|
[Serializable]
|
public sealed class DriverMethodInfo
|
{
|
/// <summary>方法在该类型方法表中的索引(调用时回传)</summary>
|
public int Index { get; set; }
|
|
public string Name { get; set; }
|
|
/// <summary>可读签名,例如 int ReadStatus(int channel)</summary>
|
public string Signature { get; set; }
|
|
public bool IsStatic { get; set; }
|
|
public int ParameterCount { get; set; }
|
|
/// <summary>参数列表(类型名 + 参数名),用于界面提示</summary>
|
public string[] Parameters { get; set; }
|
}
|
|
/// <summary>
|
/// 反射调用结果。
|
/// </summary>
|
[Serializable]
|
public sealed class InvokeResult
|
{
|
public bool Success { get; set; }
|
|
/// <summary>返回值文本(成功时)</summary>
|
public string Output { get; set; }
|
|
/// <summary>错误信息(失败时)</summary>
|
public string Error { get; set; }
|
}
|
|
/// <summary>
|
/// 直接调用结果:保留原始返回值与 out/ref 回填值,
|
/// 供 RealProxy 动态代理把驱动调用适配成强类型接口方法。
|
/// 注意:Value / ArgsOut 只能携带可跨域封送的数据(基元类型、string、[Serializable] DTO)。
|
/// </summary>
|
[Serializable]
|
public sealed class InvokeDirectResult
|
{
|
public bool Success { get; set; }
|
|
/// <summary>方法返回值(void 方法为 null)</summary>
|
public object Value { get; set; }
|
|
/// <summary>调用后的参数数组(out/ref 位置为回填值,其余位置原值)</summary>
|
public object[] ArgsOut { get; set; }
|
|
/// <summary>错误信息(失败时)</summary>
|
public string Error { get; set; }
|
}
|
|
/// <summary>
|
/// 运行在每个驱动包专属 AppDomain 内的加载器。
|
///
|
/// 设计前提:驱动 dll 来自不同厂商,彼此没有任何共同接口/基类约定,
|
/// 甚至互不兼容(同一第三方依赖的不同版本)。
|
/// 因此这里只做通用反射:枚举公开类型、枚举公共方法、反射创建实例、反射调用。
|
///
|
/// 该域的 ApplicationBase 被设置为某个驱动子文件夹,
|
/// 所以驱动的全部依赖只能从自己目录解析,天然隔离版本冲突。
|
/// </summary>
|
public class DriverLoader : MarshalByRefObject
|
{
|
private static readonly HashSet<string> FailedAssemblies =
|
new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
private readonly Dictionary<string, Type> _types = new Dictionary<string, Type>(StringComparer.Ordinal);
|
private readonly Dictionary<string, MethodInfo[]> _methodCache = new Dictionary<string, MethodInfo[]>(StringComparer.Ordinal);
|
private readonly Dictionary<Type, object> _instances = new Dictionary<Type, object>();
|
|
/// <summary>由 DriverManager 创建、按句柄管理的驱动实例</summary>
|
private readonly Dictionary<string, ManagedInstance> _managedInstances =
|
new Dictionary<string, ManagedInstance>(StringComparer.Ordinal);
|
|
/// <summary>驱动索引:(类型, 品牌) → 驱动信息,key 形如 "CAN|BA"</summary>
|
private readonly Dictionary<string, DriverTypeInfo> _driverIndex =
|
new Dictionary<string, DriverTypeInfo>(StringComparer.OrdinalIgnoreCase);
|
|
// 无限生存期,避免远程代理被回收
|
public override object InitializeLifetimeService() => null;
|
|
public DriverLoader()
|
{
|
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
|
}
|
|
#region 发现
|
|
/// <summary>
|
/// 扫描本驱动目录下的所有 dll(忽略宿主自身的 MultiPlugin.* 运行时文件),
|
/// 返回每个程序集的信息与公开类型列表。
|
/// </summary>
|
public DriverAssemblyInfo[] LoadAssemblies()
|
{
|
var dir = AppDomain.CurrentDomain.BaseDirectory;
|
var result = new List<DriverAssemblyInfo>();
|
|
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<DriverTypeInfo>();
|
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();
|
}
|
|
/// <summary>
|
/// 按 (类型, 品牌) 查找驱动。任一字段留空视为通配。
|
/// </summary>
|
/// <returns>命中的第一个驱动信息;未命中返回 null</returns>
|
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;
|
}
|
|
/// <summary>
|
/// 用鸭子反射读取 [DriverDesc] 注解的 Type/Brand 属性值。
|
/// 宿主基础设施不引用 Driver.Core,只按"属性名"解耦读取,
|
/// 这样厂商 SDK 升级/换版本不会影响宿主。
|
/// </summary>
|
private static Tuple<string, string> 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}";
|
}
|
|
/// <summary>
|
/// 列出某个类型的公共方法(属性访问器会被过滤掉)。
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 创建驱动类型的新实例(不走 Invoke 的默认缓存实例),按句柄登记供 DriverManager 管理。
|
/// </summary>
|
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);
|
}
|
|
/// <summary>
|
/// 在指定句柄的驱动实例上按"原始对象参数"直接调用方法,
|
/// 保留原始返回值与 out/ref 回填值(供 RealProxy 动态代理使用)。
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 在指定句柄的驱动实例上反射调用方法(实际执行发生在驱动自己的域内)。
|
/// </summary>
|
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);
|
}
|
|
/// <summary>
|
/// 释放指定句柄的实例;驱动实现 IDisposable 时会调用 Dispose。
|
/// </summary>
|
public void ReleaseInstance(string handle)
|
{
|
if (!_managedInstances.TryGetValue(handle, out var managed))
|
{
|
return;
|
}
|
|
_managedInstances.Remove(handle);
|
(managed.Target as IDisposable)?.Dispose();
|
}
|
|
#endregion
|
|
#region 调用
|
|
/// <summary>
|
/// 反射调用指定类型的方法。
|
/// 实例方法会自动用公共无参构造函数创建(并缓存)实例;
|
/// 静态方法无需实例。需要长期持有实例请使用 <see cref="CreateInstance"/>。
|
/// </summary>
|
/// <param name="rawArgs">字符串形式的实参,由 <see cref="ConvertArg"/> 按目标参数类型转换</param>
|
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}");
|
}
|
}
|
|
/// <summary>
|
/// 参数转换 → 反射调用 → 汇总返回值与 out/ref 回填值。
|
/// </summary>
|
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<string>();
|
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}>";
|
}
|
|
/// <summary>
|
/// 把跨域代理传来的对象参数适配为目标参数类型:
|
/// 类型兼容直接用,否则退化为字符串转换(鸭子式宽松匹配)。
|
/// </summary>
|
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);
|
}
|
|
/// <summary>
|
/// 把 UI 传来的字符串转换为目标参数类型。
|
/// 支持常用基元类型、枚举、Guid、DateTime、字符串数组(以分号分隔)。
|
/// </summary>
|
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<object>().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 };
|
|
/// <summary>
|
/// 依赖解析兜底:只在本驱动目录(及加载器所在目录)内查找,
|
/// 保证不会串到其他驱动包的同名程序集。
|
/// </summary>
|
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<string>();
|
|
// 优先按加载器所在目录(宿主 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
|
}
|
}
|