I have a question related to the code provided in an answer to this question.
The problem I have is that the three referenced assmeblies (System.dll, FSharp.Core.dll, FSharp.Powerpack.dll) that are passed to CompilerParameters are not found at runtime. The error I get is:
unknown-file(0,0) : error 0: error FS0218: Unable to read assembly
‘c:\user s\utente\documents\visual studio
2010\Projects\TrashSolution\TrashSolution\bin\D ebug\FSharp.Core.dll’
How do I tell the compiler to search for these assemblies in the GAC, instead of the project’s bin directory? If I open a namespace in the code provided as a string, how do I know which assemblies to add? Where can I get this information?
In the code from the answer you linked, there’s a line towards the bottom:
If you call
Reflection.Loadinstead and pass it the fully-qualified assembly name, it’ll try to load the assembly from the GAC (and a few other places, if the assembly isn’t in the GAC).If you don’t know the fully-qualified assembly name you have to create an
AssemblyNamewith the simple name of the assembly, then call theReflection.Loadoverload which takes anAssemblyNameinstead of astring.As far as knowing which assemblies to load — I don’t think there’s a simple way to determine that programmatically. The only two solutions I can think of right now:
FSharpCodeProviderand look at which namespaces/modules are opened and which types are used. If you’re looking to see if some particular namespace or type is used (i.e., that you would need to include an assembly reference for when compiling the code), you could create a Map (in your.fsxwhich is doing the compilation) of namespaces and/or type names to assembly names and use it to reference the appropriate assemblies.Assembly.ReflectionOnlyLoadmethod to load the assemblies! This allows the assemblies to be unloaded after you finish examining them — if you use normal Reflection the assemblies can’t be unloaded and your program will likely crash with anOutOfMemoryExceptionor similar.EDIT: Turns out that loading the assembly by its simple name succeeds in
fsiand not in normal F# code becausefsiautomatically installs a handler for the AppDomain.AssemblyResolve event. This event is triggered by the CLR when you try to load an assembly and it can’t be resolved; the event provides a way for you to “manually” resolve the assembly and/or generate an assembly dynamically and return it.If you look at the
FileNotFoundExceptionraised when you try to run the code in an F# project, you’ll see something like this in the Fusion Log property of the exception:Looking towards the bottom of that log, you’ll see where the CLR searched for the assembly before it gave up.
Here’s a simple handler to give you an idea of how to use the AppDomain.AssemblyResolve handler to manually resolve the assembly. (NOTE: The handler needs to be added before the code that attempts to load the assembly!)
If you add that code to a new F# console project, followed by the code which uses
AssemblyNamewith Assembly.Load, you should be able to load theSystemassembly because it’s referenced by default in an F# project and it’ll be loaded when you run the project. If you try to resolveSystem.Drawing, it’ll fail because our custom event handler can’t find the assembly. Obviously, if you need some more complicated assembly-resolving logic, you should build that into the event handler in whatever way makes sense for your application.Finally, here’s a link to the MSDN whitepaper mentioned in the exception message: Best Practices for Assembly Loading. It’s worth a read if you get stuck and can’t figure out how to resolve the assemblies you need.