5
votes

i am trying to compile some code to memory at runtime using a CodeDomProvider.

The code I am compiling have a reference to an external assembly which I include in the parameters used when compiling the code.

When I compile to memory and try to use reflection on the assembly generated in a Visual Studio Add-In it throws an exception saying that it can't find the referenced assembly.

(Exception)
"Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information."

(LoaderException)
"{"Could not load file or assembly 'Dynamo.Jiss.Task, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.":"Dynamo.Jiss.Task, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"}"

I have tried referencing the Assembly from different places using the absolute path.

The exact same code works fine if it is run from a Console application, and it also works fine in the Add-In if I do not compile to memory.
Removing the reference to the external assembly and the code that reference it also works when compiling to memory, so it probably as the exception describes is a problem with loading the referenced assembly.

Do anyone have an idea why compiling to memory and referencing an assembly isnt working in an Add-In ?

Is there some restrictions within the AppDomain it is running or something that I should be aware of? (my best guess currently)

Should it be in a specific folder? referenced using a relative path? security settings? needs to be signed? any ideas?


What i am trying to achieve is a way to put files with a specific extension in a project and let the addin automatically compile it and if it implements an ITask interface (from the external assembly) it will call a Setup() method that makes it possible for the code to hook into the Visual Studio events and execute tasks/scripts while listening to the different events. This way I can easily execute Text templates if another file is changed, or Combine and Minify files on different events (document saved, build etc.).

Does something like this already exist (to relieve me from the pain) ? :)

1

1 Answers

9
votes

This is most likely happening because you're telling CodeDom to generate an in-memory assembly (which is really a lie since it's generating temporarily to disk, loading it, then deleting the file). The point is, the compile directory for the CodeDom assembly is not the same as the one you're using to compile it. That is, if you're running in bin\Debug, the CodeDom assembly is being generated to %temp%.

You could solve this in one of two ways I can think of:

  1. Compile the CodeDom assembly to the same path as your executing assembly.

    myCodeProvider.GenerateInMemory = false; // may not be necessary...haven't tried this in a while
    myCodeProvider.OutputAssembly = string.Format(@"{0}\{1}", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location, "mydll.dll");
    
  2. Handle the AssemblyResolve event and provide the CodeDom assembly the referenced assembly it's requested.

    AppDomain.CurrentDomain.AssemblyResolve += OnCurrentDomainAssemblyResolve
    
    private static Assembly OnCurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
    {
                    // this is absurdly expensive...don't do this more than once, or load the assembly file in a more efficient way
                    // also, if the code you're using to compile the CodeDom assembly doesn't/hasn't used the referenced assembly yet, this won't work
                    // and you should use Assembly.Load(...)
                    foreach (Assembly @assembly in AppDomain.CurrentDomain.GetAssemblies())
                    {
                        if (@assembly.FullName.Equals(args.Name, StringComparison.OrdinalIgnoreCase))
                        {
                            return @assembly;
                        }
                    }
    }