open System open System.Diagnostics open System.IO open System.Reflection module private ValueOption = let mapNone initializer = function | ValueSome value -> ValueSome value | ValueNone -> initializer () /// /// Considers a path to load for satisfying an assembly ref and loads it /// if the file exists and version is sufficient. /// /// Path to consider for load /// Minimum version to consider /// loaded assembly voption let private probe(filePath: string, minimumVersion: Version) : Assembly voption = if File.Exists(filePath) then let name = AssemblyName.GetAssemblyName(filePath) if name.Version >= minimumVersion then ValueSome (Assembly.Load(name)) else ValueNone else ValueNone let private loadFromCurrentDirectory fileName version = Debug.WriteLine($"Considering {fileName}") probe(fileName, version) let private loadFromExecutingAssembly fileName version = match Assembly.GetExecutingAssembly().Location with | null -> ValueNone | assemblyPath -> let probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName) Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly") probe(probingPath, version) let private loadFromAppDomain fileName version = let probingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName) Debug.WriteLine($"Considering {probingPath} based on BaseDirectory") probe(probingPath, version) let private loadFromRequestingAssemblyPath version fileName assemblyPath = let probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName) Debug.WriteLine($"Considering {probingPath} based on RequestingAssembly") probe(probingPath, version) let private onAssemblyResolve(args: ResolveEventArgs) : Assembly = // apply any existing policy let referenceName = new AssemblyName(AppDomain.CurrentDomain.ApplyPolicy(args.Name)) let version = referenceName.Version let fileName = referenceName.Name + ".dll" args.RequestingAssembly |> ValueOption.ofObj |> ValueOption.map (fun assemblyPath -> assemblyPath.Location) |> ValueOption.bind (loadFromRequestingAssemblyPath version fileName) |> ValueOption.mapNone (fun () -> loadFromAppDomain fileName version) |> ValueOption.mapNone (fun () -> loadFromExecutingAssembly fileName version) |> ValueOption.mapNone (fun () -> loadFromCurrentDirectory fileName version) |> ValueOption.defaultValue null [] let public enable() = AppDomain.CurrentDomain.add_AssemblyResolve(fun _ args -> onAssemblyResolve(args))