Suite

CodeDom.Compiler échoue dans le complément Arcmap, mais fonctionne à partir de l'application console

CodeDom.Compiler échoue dans le complément Arcmap, mais fonctionne à partir de l'application console


La méthode Test dans le code ci-dessous réussit dans une application de test de console, mais échoue lorsque je l'appelle depuis un complément Arcmap, en lançant une ReflectionTypeLoadException avec une exception de chargeur indiquant :

Impossible de charger le fichier ou l'assembly 'KompilerLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' ou l'une de ses dépendances. Le système ne peut pas trouver le fichier spécifié.

KompilerLib est un projet de bibliothèque de classes Windows (3.5) et c'est le seul fichier du projet (une interface et une classe).

Y a-t-il quelque chose de spécial que je dois faire si j'utilise CodeDom.Compiler dans un complément ?

en utilisant le système ; en utilisant System.Collections.Generic ; en utilisant System.Text; en utilisant System.CodeDom.Compiler ; en utilisant System.Diagnostics ; en utilisant System.IO; espace de noms KompilerLib { interface publique IKalkulation { string Test(); } classe publique Kompiler { chaîne const privée TYPENAME = "Kalk.Kalkulation"; public IKalkulation Kompile (langue de chaîne, liste références, chaîne source) { Fournisseur CodeDomProvider = CodeDomProvider.CreateProvider(langue); var parameters = new CompilerParameters(); parameters.GenerateInMemory = true; parameters.GenerateExecutable = false; foreach (référence de chaîne dans les références) { parameters.ReferencedAssemblies.Add(reference); } var result = provider.CompileAssemblyFromSource(parameters, source); if (results.Errors == null || results.Errors.Count == 0) { Debug.Print(results.CompiledAssembly.ReflectionOnly.ToString()); foreach (Tapez t dans results.CompiledAssembly.GetTypes()) Debug.Print(t.Name); var type = results.CompiledAssembly.GetType(TYPENAME); if (type == null) throw new Exception("type not found: " + TYPENAME); objet o = Activator.CreateInstance(type); if (o == null) throw new Exception("unable to createinstance"); var kalkulation = o comme IKalkulation ; if (kalkulation == null) throw new Exception("impossible de transtypage vers IKalkulation"); kalkulation de retour; } else { StringBuilder sb = new StringBuilder(); foreach (CompilerError err in results.Errors) sb.AppendLine(err.ErrorText); lancer une nouvelle exception(sb.ToString()); } } chaîne statique publique Test() { var kompiler = new Kompiler(); var liste = nouvelle liste(); list.Add("System.dll"); chemin de chaîne = kompiler.GetType().Assembly.Location; if (!File.Exists(path)) throw new Exception("file not found" + path); list.Add(chemin); chaîne source = GetSource(); var kalk = kompiler.Kompile("CSharp", liste, source); return kalk.Test(); } chaîne statique privée GetSource() { StringBuilder sb = new StringBuilder(); sb.AppendLine("en utilisant KompilerLib; "); sb.AppendLine("espace de noms Kalk "); sb.AppendLine("{ "); sb.AppendLine(" public class Kalkulation : IKalkulation "); sb.AppendLine(" { "); sb.AppendLine(" chaîne publique Test() "); sb.AppendLine(" { "); sb.AppendLine(" return "Hello World"; "); sb.AppendLine(" } "); sb.AppendLine(" } "); sb.AppendLine("} "); return sb.ToString(); } } }

Mettre à jour

Voici le correctif en utilisant AssemblyResolve, grâce à blah238. Je suis toujours curieux de savoir pourquoi c'est un problème dans arcmap.exe, mais pas dans mon exe de testeur de console. Il semble également que l'assembly le plus facile à résoudre devrait être celui dans lequel le compilateur s'exécute.

public IKalkulation Kompile (langue de chaîne, liste références, chaîne source) { Fournisseur CodeDomProvider = CodeDomProvider.CreateProvider(langue); var parameters = new CompilerParameters(); parameters.GenerateInMemory = true; parameters.GenerateExecutable = false; foreach (référence de chaîne dans les références) { parameters.ReferencedAssemblies.Add(reference); } // cela ressemble bien à un hack… AppDomain.CurrentDomain.AssemblyResolve += (s,args) => { if (args.Name == this.GetType().Assembly.FullName) return this.GetType().Assembly ; sinon renvoie null ; } ; var result = 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); objet o = Activator.CreateInstance(type); if (o == null) throw new Exception("unable to createinstance"); var kalkulation = o comme IKalkulation ; if (kalkulation == null) throw new Exception("impossible de transtypage vers IKalkulation"); kalkulation de retour; } else { StringBuilder sb = new StringBuilder(); foreach (CompilerError err in results.Errors) sb.AppendLine(err.ErrorText); lancer une nouvelle exception(sb.ToString()); } }

J'ai rencontré des problèmes similaires lors de l'utilisation de .NET personnaliséSection Configuration's et lors de l'utilisation de la (dé)sérialisation binaire. Ce problème est discuté dans les commentaires de cette question : Complément ArcMap avec app.settings ne reconnaissant pas les modifications app.config ?

Le problème semble être que certains assemblys référencés par des compléments ne peuvent pas être résolus correctement car le domaine d'application du complément n'est pas séparé du domaine d'application principal et donc le runtime .NET regarde dans le chemin de sondage de l'assembly de l'application principale et ne les trouve pas. Le correctif est d'utiliser Assemblage.ChargerDe et gérer le AppDomain.AssemblyResolve un événement.

C'est le correctif qui a fonctionné pour moi avec la coutumeSection Configurationpublier. J'espère que ça marchera aussi dans ton cas !


Voir la vidéo: ArcGIS Desktop: New HTML Popup Tool