DirectoryLookupModuleEnumerator error on loading duplicate dll!

Aug 27, 2008 at 12:10 PM
Edited Aug 27, 2008 at 12:39 PM
Hi there,
Currently, i'm using DirectoryLookupModuleEnumerator for asm loading. The problem arise when I duplicate a dll in my plugin directory.
The error is: "File load exception: API restriction: The assembly 'file:///D:\Visual Studio 2008\Projects\WestSoft7\WestSoft7\bin\Debug\Plugin\testComposeUIModule.dll' has already loaded from a different location. It cannot be loaded from a new location within the same appdomain."

Stack trace:
"   at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)\r\n   at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)\r\n   at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)\r\n   at System.Reflection.Assembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, StackCrawlMark& stackMark)\r\n   at System.Reflection.Assembly.ReflectionOnlyLoadFrom(String assemblyFile)\r\n   at Microsoft.Practices.Composite.Modularity.DirectoryLookupModuleEnumerator.InnerModuleInfoLoader.<>c__DisplayClass12.<GetModuleInfos>b__e(FileInfo file) in C:\\ComposeUI\\Source\\CAL\\Composite\\Modularity\\DirectoryLookupModuleEnumerator.cs:line 180\r\n   at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()\r\n   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)\r\n   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)\r\n   at Microsoft.Practices.Composite.Modularity.DirectoryLookupModuleEnumerator.InnerModuleInfoLoader.GetModuleInfos(String path) in C:\\ComposeUI\\Source\\CAL\\Composite\\Modularity\\DirectoryLookupModuleEnumerator.cs:line 186\r\n   at Microsoft.Practices.Composite.Modularity.DirectoryLookupModuleEnumerator.InnerModuleInfoLoader.GetModuleInfos(String path)\r\n   at Microsoft.Practices.Composite.Modularity.DirectoryLookupModuleEnumerator.EnsureModulesDiscovered() in C:\\ComposeUI\\Source\\CAL\\Composite\\Modularity\\DirectoryLookupModuleEnumerator.cs:line 113\r\n   at Microsoft.Practices.Composite.Modularity.DirectoryLookupModuleEnumerator.GetStartupLoadedModules() in C:\\ComposeUI\\Source\\CAL\\Composite\\Modularity\\DirectoryLookupModuleEnumerator.cs:line 71\r\n   at Microsoft.Practices.Composite.UnityExtensions.UnityBootstrapper.InitializeModules() in C:\\ComposeUI\\Source\\CAL\\Composite.UnityExtensions\\UnityBootstrapper.cs:line 174\r\n   at Microsoft.Practices.Composite.UnityExtensions.UnityBootstrapper.Run(Boolean useDefaultConfiguration) in C:\\ComposeUI\\Source\\CAL\\Composite.UnityExtensions\\UnityBootstrapper.cs:line 106\r\n   at Microsoft.Practices.Composite.UnityExtensions.UnityBootstrapper.Run() in C:\\ComposeUI\\Source\\CAL\\Composite.UnityExtensions\\UnityBootstrapper.cs:line 66\r\n   at TinLong.Application..ctor() in D:\\Visual Studio 2008\\Projects\\WestSoft7\\WestSoft7\\Application.xaml.vb:line 8\r\n   at TinLong.Application.Main() in D:\\Visual Studio 2008\\Projects\\WestSoft7\\WestSoft7\\obj\\Debug\\Application.g.vb:line 67\r\n   at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)\r\n   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)\r\n   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()\r\n   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)\r\n   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)\r\n   at System.Threading.ThreadHelper.ThreadStart()"

Any suggestion? I want to detect if there is any error, this module loader just skip it, so my program could continue.
Aug 27, 2008 at 12:29 PM
More information on this error: when trying to debug, i've found where the error come from:

        class InnerModuleInfoLoader : MarshalByRefObject
        {
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
            internal ModuleInfo[] GetModuleInfos(string path)
            {
                DirectoryInfo directory = new DirectoryInfo(path);

                ResolveEventHandler resolveEventHandler =
                    delegate(object sender, ResolveEventArgs args)
                    {
                        Assembly loadedAssembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(
                                asm => string.Equals(asm.FullName, args.Name, StringComparison.InvariantCultureIgnoreCase));
                        if (loadedAssembly != null)
                        {
                            return loadedAssembly;
                        }
                        AssemblyName assemblyName = new AssemblyName(args.Name);
                        string dependentAssemblyFilename = Path.Combine(directory.FullName, assemblyName.Name + ".dll");
                        if (File.Exists(dependentAssemblyFilename))
                        {
                            return Assembly.ReflectionOnlyLoadFrom(dependentAssemblyFilename);
                        }
                        return Assembly.ReflectionOnlyLoad(args.Name);
                    };

                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;

                Assembly moduleReflectionOnlyAssembly =
                    AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().First(
                        asm => asm.FullName == typeof(IModule).Assembly.FullName);
                Type IModuleType = moduleReflectionOnlyAssembly.GetType(typeof(IModule).FullName);

                Assembly[] alreadyLoadedAssemblies = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies();
                var modules = directory.GetFiles("*.dll")
                    .Where(file => alreadyLoadedAssemblies
                        .FirstOrDefault(assembly => String.Compare(Path.GetFileName(assembly.Location), file.Name, StringComparison.OrdinalIgnoreCase) == 0) == null)
                    .SelectMany(file => Assembly.ReflectionOnlyLoadFrom(file.FullName)
                                            .GetExportedTypes()
                                            .Where(IModuleType.IsAssignableFrom)
                                            .Where(t => t != IModuleType)
                                            .Select(type => CreateModuleInfo(type)));
<-- using dedug mode, this line of code make error

                var array = modules.ToArray();
                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;
                return array;
            }
Aug 28, 2008 at 5:23 AM
Hi,

The only option that works for solving these kind of conflicts is by implementing your own module enumerator that only prints a warning in the log and skips loading the module.
Although you could argue that the behavior of the directory module enumerator is a bit strange, it is correct because you normally don't want to load duplicate modules.

Checkout the CompositeWPFContrib project for a sample on how to build your own module enumerator, or check this weblog post on the composite module enumerator.
It contains a more elaborate description of how you can approach the problem.

I hope this helps.
Aug 29, 2008 at 12:29 AM
Thanks for your replay, i'll check it out ;)