Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have a managed C++ assembly I'm loading dynamically in an unmanaged c++ application through a standard LoadLibrary() call. The managed C++ assembly has dependencies on several more managed (C#) assemblies. Everything worked fine until I moved all the managed assemblies to a subdirectory of the unmananged application. To illustrate:

  • Managed C++ .dll (MyCoolDll.dll)

  • Dependent on DotNetDll1.dll
  • Dependent on DotNetDll2.dll
  • Unmanaged C++ app (MyCoolApp.exe)

  • Loads MyCoolDll.dll via LoadLibrary("MyCoolDll.dll")
  • This worked fine, until I moved MyCoolDll.dll, DotNetDll1.dll & DotNetDll2.dll to /someSubDirectory (the code in MyCoolApp.exe was updated to LoadLibrary("someSubDirectory/MyCooldll.dll")

    I'm guessing when MyCoolDll.dll is loaded, it's trying to find DotNetDll1.dll and DotNetDll2.dll in the working directory, instead of the directory it lives in.

    How can I tell MyCoolDll.dll its dependencies live in a subdirectory? It's a library running inside of an unmanaged app, so I don't think I can specify this in an app.config or anything?

    Wow, Hans, that worked! I was really dubious since MyCoolApp.exe is just a plain old Win32 application (not .NET), so I figured adding an app config file for it wouldn't help. Thanks! Do you want to write this up as an answer instead of a comment, and I'll mark it as accepted? Jordan0Day Aug 11, 2011 at 18:27

    I think what you're looking for is a custom assembly resolver. I had to use one to do what I think you are trying to do -- I wanted to locate some of the DLLs in a folder that wasn't in the tree of the initial unmanaged DLL (which loaded managed code eventually).

    Step 1 is to make a function you can call to set up the resolver:

    void PrepareManagedCode()
        // Set up our resolver for assembly loading
        AppDomain^ currentDomain = AppDomain::CurrentDomain;
        currentDomain->AssemblyResolve += gcnew ResolveEventHandler(currentDomain_AssemblyResolve);
    }  // PrepareManagedCode()
    

    Then the resolver. This example has a global ourFinalPath which would in your case be the extra folder you were using:

    /// <summary>
    /// This handler is called only when the CLR tries to bind to the assembly and fails
    /// </summary>
    /// <param name="sender">Event originator</param>
    /// <param name="args">Event data</param>
    /// <returns>The loaded assembly</returns>
    Assembly^ currentDomain_AssemblyResolve(Object^ sender, ResolveEventArgs^ args)
        sender;
        // If this is an mscorlib, do a bare load
        if (args->Name->Length >= 8 && args->Name->Substring(0, 8) == L"mscorlib")
            return Assembly::Load(args->Name->Substring(0, args->Name->IndexOf(L",")) + L".dll");
        // Load the assembly from the specified path
        String^ finalPath = nullptr;
            finalPath = gcnew String(ourAssemblyPath) + args->Name->Substring(0, args->Name->IndexOf(",")) + ".dll";
            Assembly^ retval = Assembly::LoadFrom(finalPath);
            return retval;
        catch (...)
        return nullptr;
                    This is the path I had figured I would have to go down -- the problem seemed to be that I was still required to have MyCoolDll.dll's dependencies in the main app directory, since .net wouldn't search the subfolder for them before even loading up MyCoolDll.dll (so the assembly resolver wouldn't get set up first). To my amazement, Hans suggestion above works (even though MyCoolApp.exe isn't a managed executable.)
    – Jordan0Day
                    Aug 11, 2011 at 18:30
                    Your unmanaged DLL will load before an assembly resolve is needed.  You can then load the mixed mode DLL yourself, and call the assembly resolve setup.  For complex scenarios you'd need it; however, if a simple config file works for your case, great!
    – Ed Bayiates
                    Aug 11, 2011 at 21:37
                    The problem I was seeing was my mixed mode dll had dependencies on a few other .NET assemblies -- so unless I placed those dependencies in the main directory instead of the subfolder, I wouldn't be able to get to any executable code in my mixed mode dll where I could wire up an assembly resolver.
    – Jordan0Day
                    Aug 12, 2011 at 13:47
                    Thanks, this works, but I have some remarks/questions: - Why do you do something special with mscorlib? It seems to work without doing anything - using AssemblyName to parse the args->Name would be better - Be careful to return the assembly actually requested, and not always the same one. I neglected to do that and bad things ensued :)
    – Melvyn
                    Aug 15, 2017 at 18:13
                    @Yaurthek This was an example from working code to show you can choose where to load from in this call.  In our case we wanted to load those libraries from a different place.
    – Ed Bayiates
                    Aug 16, 2017 at 14:59
    

    The CLR gets loaded in an unusual way in this scenario, through a thunk that the compiler injected when compiling the native export for __declspec(dllexport). Doing this is fine, it just isn't particularly fast.

    The CLR will go out hunting for a .config file to initialize the primary AppDomain. And will look for MyCoolApp.exe.config, regardless that this is not a managed executable at all. You can use the <probing> element to add subdirectories to search for assemblies.

    I had this problem, but also a catch-22 in that my managed C++ DLL would immediately crash because it directly referenced the .NET DLLs that it could not find. I could not even call any .NET code at all, so Ed's solution did not initially work for me.

    The trick was to create a second managed C++ DLL, with no references to any special .NET DLLs. Without these references it loads fine. In that second DLL, run Ed's code to set up the assembly resolver. Then load the problematic, managed C++ DLL, as before, and it works fine.

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.