midspace
midspace

Reputation: 964

Avoiding Loading Multiple Versions of an Assembly into the Same Context

The topic sort of covers my issue. I've seen the Best practise document about the issue, but it doesn't really describe how to get out of the situation. http://msdn.microsoft.com/en-us/library/dd153782.aspx

My situation. Path A: Main Application. (Signed, versioned. .net 3.5) Common Interface Assembly X. (Signed, versioned)

Path B: User Assembly. Common Interface Assembly X. (Signed, versioned) (Potentially a different version than above. May not know the actual version, as we can't control what the customer/user does.)

Our Main application loads a specified class from the User Assembly using reflection, and communicates with the class using a specified interface defined in the Common Interface Assembly X. Applications have to remain in one particular path, and user (Business) assemblies in another path. Don't ask me to change this, it's out of my hands.

The problem is, there most likely will be a copy of the Common Interface Assembly X in Path B. So when I load the user Assembly using reflection, it automatically loads Common Interface Assembly X from the same path, instead of using the one already loaded in memory with the Application. I ended up with Type iXXX isn't the same as Type iXXX, or cannot find Constructor for the User Assembly class, because again, it's confused the interfaces between the two copies of Common Interface Assembly X.

So, how can I go about loading, and creating a class from User Assembly in Path B, but not have it load the Common Interface Assembly X from Path B (and use the one from Path A, already in memory)?

More Info: Most problems I'm getting occur around CreateInstance, as the ClientContext is in the Common Interface Assembly X, so whilst I've created one from Path A, the Constructor on my dialog seems to be expecting the one from Path B. I then end up with a "Constructor on type 'ExternalTestForms.WindowDemo1' not found." error. (Even with the exact same [signed and versioned] assembly in both Path A and Path B, this error is still happening.)

I think this covers the code I'm using to load the assembly and create the custom Window using the interface.

        public IExternalForm ShowExternalWindow(string filename, string classname, string parameters, string formIdentity)
        {
            if (File.Exists(filename))
            {
                ClientContext context = GetCurrentClientContext(parameters, formIdentity);

                object dialog = null;

                AppDomain currentDomain = AppDomain.CurrentDomain;
                currentDomain.AssemblyResolve += this.CurrentDomain_AssemblyResolve;

                this.basePath = Path.GetDirectoryName(filename);
                Assembly externalAssembly = Assembly.LoadFile(filename);

                try
                {
                    // The Window must must have a ClientContext defined in the constructor.
                    dialog = externalAssembly.CreateInstance(classname, false, BindingFlags.CreateInstance, null, new object[] { context }, context.Desktop.Culture, null);
                }
                catch (Exception ex)
                {
                    // MessageBox "Could not launch the Window"
                    // LogTraceException
                }

                if (dialog != null && dialog is IExternalForm && dialog is System.Windows.Window)
                {
                    System.Windows.Window window = (System.Windows.Window)dialog;
...
...
...
                }
...
            }
        }


        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string filename = args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";

            // Seek out the specified assembly in the current Application path first.
            string assemblyPath = Path.Combine(Application.StartupPath, filename);

            if (!File.Exists(assemblyPath))
            {
                assemblyPath = Path.Combine(this.basePath, filename);
            }

            if (File.Exists(assemblyPath))
            {
                // Load the assembly from the specified path.
                Assembly myAssembly = Assembly.LoadFrom(assemblyPath);

                // Return the loaded assembly.
                return myAssembly;
            }

            return null;
        }

Upvotes: 0

Views: 2104

Answers (1)

competent_tech
competent_tech

Reputation: 44921

A couple of different low-impact options to consider:

1) At application startup, copy customer assemblies only into main folder (we require our customer assemblies to have a specific naming convention in order to be loaded, so this is easy for us to implement).

2) At application startup, delete any copies of the standard DLLs in the customer directories to prevent conflicts.

Upvotes: 1

Related Questions