开发者

Loading Mixed-Mode C++/CLI .dll (and dependencies) dynamically from unmanaged c++

I have a managed C++ assembly I'm loading dynamically in an unmanaged c++ application through a standard LoadLibrary(开发者_StackOverflow) 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?


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;
    try
    {
        finalPath = gcnew String(ourAssemblyPath) + args->Name->Substring(0, args->Name->IndexOf(",")) + ".dll";
        Assembly^ retval = Assembly::LoadFrom(finalPath);
        return retval;
    }
    catch (...)
    {
    }

    return nullptr;
}


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜