Lazy load a DLL that was placed on the filesystem

2019-08-14 16:34发布

问题:

I am building an automation harness using C# and am trying to do the following:

  • Bootstrap the harness
  • Install the executable
  • Use a couple of DLLs that the executable lays down to establish a connection to the infrastructure that the exe connects to (large, complex software system)

The DLLs that I need to use are a few static classes that perform their own 'bootstrap' process to get connected to the rest of the servers in the test environment. What I have tried is creating a connection class that looks like this:

using CompanyName.Framework;

namespace TestNamespace{
    public class ProductConnectorClass{
        // Initialize a connection to the product and do stuff
        public ProductConnectorClass(){
            var connection = CompanyName.Framework.Initialize(...);

            // Do stuff with the connection
            connection.RunStuff();
        }
    }
}

The DLL that contains the CompanyName.Framework namespace is not accessible when the test framework is first started. It is copied via code from another class that looks lomething like this:

namespace TestNamespace{
    public class TestRunnerClass{
        public TestRunnerClass(){
            // pseudo code here, so you get the idea:
            CopyMsiToHost(msiRemotePath, msiLocalPath);
            InstallMsi(msiLocalPath);
            CopyDllsToTestHarnessDir();
            ProductConnectorClass pcc = new ProductConnectorClass();
        }
    }
}

It is when the code hits the ProductConnectorClass pcc = new ProductConnectorClass(); line that I get an exception:

FileNotFoundException was unhandled
Could not load file or assembly 'CompanyName.Framework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXXXXXXXX' or one of its dependencies. The system cannot find the file specified.

This puzzles me since I have set Specific Version: False on the DLL in my test project and I am operating under the assumption that .NET Lazy-loads DLLs (in my mind that means search for and load them at the time they are needed). At this point I am looking for a simple way to get a running .NET program to find a needed DLL that has been placed after the process started running.

Note 1: If I restart the process after the DLL is already in place, it loads fine. It looks like it is only scanning for DLLs on startup, which still puzzles me.

Note 2: This SO answer gives me some information about how DLLs are loaded, but doesn't fully answer my question. The synopsis is that if .NET has tried to load the DLL and it fails that it won't try to load it again. In my case I don't expect that a load attempt has been made on the DLL since the class where the DLL is referenced has not been instantiated yet.

回答1:

From experiments in test projects that I have performed, it appears that I need to perform modularization of my code in order to get this to work. In no case am I able to reference a dll that does not currently exist, start my program then drop down the DLL.

Here are my steps to a solution:

  • Refactor my common classes and interfaces to their own DLL project in my Automation solution
  • Create another DLL project ('TestLogic') in my solution that uses the DLL I lay down after the program has started
  • When I need to use the DLL that is laid down after the program starts, I use reflection to load the TestLogic dll and perform the tests

For reference I did a google search for 'C# late load dll' and found this msdn social post with some helpful advice. Here is a copy/paste/modify of the most helpful reply from that thread:

  1. Move all the code that references the DLLs that are installed after the test framework starts to a completely separate assembly. We'll call this "TestLogicAssembly".
  2. Create a new assembly and define an interface. We'll call this "TestInterfaceAssembly".
  3. Reference TestInterfaceAssembly in both your main DLL and your TestLogicAssembly.
  4. Create a class in TestLogicAssembly that implements the interface created in TestInterfaceAssembly.
  5. Don't reference TestLogicAssembly from your main application.
  6. At runtime, check to see if the DLLs that are laid down as part of the install step of the test framework are actually installed. If they are, use Assembly.Load to load the TestLogicAssembly, then find the type that implements the interface defined in number 2. Use Activator.CreateInstance to create an instance of this type, and cast it to the interface you created.