RobBaker
RobBaker

Reputation: 305

How do I include an absolute path to a C# assembly DLL to reference in my C# class library?

I am making an XLL using C# with ExcelDNA, and all works fine so far apart from one thing - I have to call a 3rd-party DLL (also developed in .NET in C#) and that I am certain is installed (with admin privileges) to a fixed absolute path on all user machines.

When I add a reference to the DLL to my csproj file (by Browsing to the DLL on my machine), it all seems to work (when I add the required 'using' statement), except that my csproj file adds a relative path to the HintPath field for the reference.

How do I add the DLL as a reference that will always be looked for in an absolute location?

The relevant section of my csproj file looks like (when it seems to work):

<ItemGroup>
  <Reference Include="SIMExporterEbmlImport">
    <HintPath>..\..\..\..\..\..\ProgramData\EB Bridging Tools\SimExporter\SIMExporterEbmlImport.dll</HintPath>
  </Reference>
  <Reference Include="System.Windows.Forms" />
</ItemGroup>

And then I can do this in my actual code:

using SIMExporterEbmlImport;
[...]
SIMExporterEbmlImport.MyClass obj = new SIMExporterEbmlImport.MyClass();

How do I go about ensuring:

  1. An absolute path is used (to the reference DLL) at runtime by whatever machine is running my XLL.
  2. A local copy is not made of the DLL, but it actually uses the pre-existing copy on each user's machine (with error handling, of course, to catch when it does not exist!).

You can probably tell by the question that I am not a professional C# developer, but trying to understand just enough to make this work. But as a consequence, if there is a simpler/more elegant solution to this issue, by all means please suggest it.

Upvotes: 0

Views: 129

Answers (1)

RobBaker
RobBaker

Reputation: 305

A very helpful (much more C-literate) colleague helped me find an answer, without my previous hacky workaround:

  1. Create an event handler for the ResolveEventHandler and ensure it is loaded before any calls to methods that need to use classes from the missing assembly:
// This field goes in one of my classes:
public static bool isAssemblyResolveRegistered = false;

// This code goes just before my method that needs to use the class in my assembly that
// I know won't be preloaded, but needs loading from a fixed location:
if (!isAssemblyResolveRegistered)
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolve);
    isAssemblyResolveRegistered = true;
}
  1. Use the following event handler code:
private static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
{ // This is an event handler for catching missing assemblies at runtime - designed to find and load the SIMExporterEbmlImport assembly and its dependencies
    
    // Assume a fixed path for where the assembly is located (can update this later to look in multiple versions if needed)
    // Should also add a switch block to only look for expected dependencies in this location,
    // and throw a helpful error if some unexpected dependency is missing.
    string dependentAssemblyName = new AssemblyName(args.Name).Name;
    string dependentAssemblyLocation = "C:\\ProgramData\\EB Bridging Tools\\SimExporter\\" + dependentAssemblyName + ".dll";


    // Try to load (will end up back here if lower dependencies are not met)
    try
    {
        return Assembly.LoadFile(dependentAssemblyLocation);
    }
    catch
    {
        return null;
    }
}

That means that I always explicitly handle unloaded dependencies at runtime, and can even supply multiple search paths if I ever need to (for different, but equally compatible, versions, for example).

My only residual issue (may open a separate question for this) is how to elegantly abort all execution with a user dialog if the DLLs are simply not present on the runtime machine.

Upvotes: 0

Related Questions