3

The Test method in the code below succeeds in a console test app, but fails when I call it from within an Arcmap add-in, throwing a ReflectionTypeLoadException with a loader exception saying:

Could not load file or assembly 'KompilerLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

KompilerLib is a windows class library (3.5) project and this is the only file in the project (one interface and one class).

Is there something special I need to do if I'm using CodeDom.Compiler inside an add-in?

using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.IO;
namespace KompilerLib
{
 public interface IKalkulation
 {
 string Test();
 }
 public class Kompiler
 {
 private const string TYPENAME = "Kalk.Kalkulation";
 public IKalkulation Kompile(string language, List<string> references, string source)
 {
 CodeDomProvider provider = CodeDomProvider.CreateProvider(language);
 var parameters = new CompilerParameters();
 parameters.GenerateInMemory = true;
 parameters.GenerateExecutable = false;
 foreach (string reference in references)
 {
 parameters.ReferencedAssemblies.Add(reference);
 }
 var results = provider.CompileAssemblyFromSource(parameters, source);
 if (results.Errors == null || results.Errors.Count == 0)
 {
 Debug.Print(results.CompiledAssembly.ReflectionOnly.ToString());
 foreach (Type t in results.CompiledAssembly.GetTypes())
 Debug.Print(t.Name);
 var type = results.CompiledAssembly.GetType(TYPENAME);
 if (type == null)
 throw new Exception("type not found: " + TYPENAME);
 object o = Activator.CreateInstance(type);
 if (o == null)
 throw new Exception("unable to createinstance");
 var kalkulation = o as IKalkulation;
 if (kalkulation == null)
 throw new Exception("unable to cast to IKalkulation");
 return kalkulation;
 }
 else
 {
 StringBuilder sb = new StringBuilder();
 foreach (CompilerError err in results.Errors)
 sb.AppendLine(err.ErrorText);
 throw new Exception(sb.ToString());
 }
 }
 public static string Test()
 {
 var kompiler = new Kompiler();
 var list = new List<string>();
 list.Add("System.dll");
 string path = kompiler.GetType().Assembly.Location;
 if (!File.Exists(path))
 throw new Exception("file not found " + path);
 list.Add(path);
 string source = GetSource();
 var kalk = kompiler.Kompile("CSharp", list, source);
 return kalk.Test();
 }
 private static string GetSource()
 {
 StringBuilder sb = new StringBuilder();
 sb.AppendLine("using KompilerLib; ");
 sb.AppendLine("namespace Kalk ");
 sb.AppendLine("{ ");
 sb.AppendLine(" public class Kalkulation : IKalkulation ");
 sb.AppendLine(" { ");
 sb.AppendLine(" public string Test() ");
 sb.AppendLine(" { ");
 sb.AppendLine(" return \"Hello World\"; ");
 sb.AppendLine(" } ");
 sb.AppendLine(" } ");
 sb.AppendLine("} ");
 return sb.ToString();
 }
 }
}

Update

Here's the fix using AssemblyResolve, thanks to blah238. I'm still curious why this is a problem within arcmap.exe, but not within my console tester exe. Also seems like the easiest assembly to resolve should be the one the compiler is running within.

public IKalkulation Kompile(string language, List<string> references, string source)
{
 CodeDomProvider provider = CodeDomProvider.CreateProvider(language);
 var parameters = new CompilerParameters();
 parameters.GenerateInMemory = true;
 parameters.GenerateExecutable = false;
 foreach (string reference in references)
 {
 parameters.ReferencedAssemblies.Add(reference);
 }
 // this sure seems like a hack ...
 AppDomain.CurrentDomain.AssemblyResolve += (s,args) =>
 {
 if (args.Name == this.GetType().Assembly.FullName)
 return this.GetType().Assembly;
 else
 return null;
 };
 var results = provider.CompileAssemblyFromSource(parameters, source);
 if (results.Errors == null || results.Errors.Count == 0)
 {
 var type = results.CompiledAssembly.GetType(TYPENAME);
 if (type == null)
 throw new Exception("type not found: " + TYPENAME);
 object o = Activator.CreateInstance(type);
 if (o == null)
 throw new Exception("unable to createinstance");
 var kalkulation = o as IKalkulation;
 if (kalkulation == null)
 throw new Exception("unable to cast to IKalkulation");
 return kalkulation;
 }
 else
 {
 StringBuilder sb = new StringBuilder();
 foreach (CompilerError err in results.Errors)
 sb.AppendLine(err.ErrorText);
 throw new Exception(sb.ToString());
 }
}
PolyGeo
65.5k29 gold badges115 silver badges349 bronze badges
asked Oct 9, 2011 at 20:24
2
  • Yeah I don't fully understand why it's needed either, but at least it works (for now). My guess is ESRI is doing something non-standard with their add-in system so the standard .NET assembly resolving behavior breaks down under certain conditions. From what I have gathered, ESRI is using Assembly.LoadFrom to load add-in assemblies manually instead of setting up a separate AppDomain for each add-in so that their probing path can be set individually and letting the .NET runtime load it normally. The side effect of this is that the probing path is incorrect for subsequent assembly loads. Commented Oct 9, 2011 at 23:10
  • I am not sure why the same assembly would need to be loaded more than once though, except that it might be for security purposes. Commented Oct 9, 2011 at 23:10

1 Answer 1

3

I have run into similar issues when using custom .NET ConfigurationSection's and when using binary (de)serialization. This problem is discussed in the comments of this question: ArcMap Add-in with app.settings not recognizing app.config changes?

The problem seems to be that some assemblies referenced by add-ins are not able to be resolved correctly because the add-in application domain is not separate from the main application domain and so the .NET runtime looks in the main application's assembly probing path and doesn't find them. The fix is to use Assembly.LoadFrom and handle the AppDomain.AssemblyResolve event.

This was the fix that worked for me with the custom ConfigurationSection issue. I hope it works in your case as well!

answered Oct 9, 2011 at 21:06
1
  • Thanks! I've updated my question with how your suggestion fixed things. Commented Oct 9, 2011 at 21:52

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.