【问题标题】:Loading Mixed-Mode C++/CLI .dll (and dependencies) dynamically from unmanaged c++从非托管 c++ 动态加载混合模式 C++/CLI .dll(和依赖项)
【发布时间】:2011-10-24 09:43:09
【问题描述】:

我有一个托管 C++ 程序集,我通过标准 LoadLibrary() 调用在非托管 c++ 应用程序中动态加载。托管 C++ 程序集依赖于多个托管 (C#) 程序集。在我将所有托管程序集移动到非托管应用程序的子目录之前,一切正常。举例说明:

  • 托管 C++ .dll (MyCoolDll.dll)

    • 依赖于 DotNetDll1.dll
    • 依赖于 DotNetDll2.dll
  • 非托管 C++ 应用 (MyCoolApp.exe)

    • 通过 LoadLibrary("MyCoolDll.dll") 加载 MyCoolDll.dll

这工作正常,直到我将 MyCoolDll.dll、DotNetDll1.dll 和 DotNetDll2.dll 移动到 /someSubDirectory(MyCoolApp.exe 中的代码已更新为 LoadLibrary("someSubDirectory/MyColdll.dll")

我猜当加载 MyCoolDll.dll 时,它试图在工作目录中找到 DotNetDll1.dll 和 DotNetDll2.dll,而不是它所在的目录。

如何告诉 MyCoolDll.dll 其依赖项位于子目录中?它是一个在非托管应用程序中运行的库,所以我认为我不能在 app.config 或任何内容中指定它?

【问题讨论】:

  • 哇,汉斯,成功了!我真的很怀疑,因为 MyCoolApp.exe 只是一个普通的旧 Win32 应用程序(不是 .NET),所以我认为为其添加应用程序配置文件无济于事。谢谢!你想把它写成答案而不是评论,我会把它标记为接受吗?

标签: .net c++ c++-cli managed-c++ loadlibrary


【解决方案1】:

我认为您正在寻找的是自定义程序集解析器。我不得不使用一个来做我认为你正在尝试做的事情——我想在一个文件夹中找到一些 DLL,该文件夹不在初始非托管 DLL(最终加载托管代码)的树中。

第 1 步是创建一个可以调用的函数来设置解析器:

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

然后是解析器。这个例子有一个全局的ourFinalPath,在你的情况下是你使用的额外文件夹:

/// <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;
}

【讨论】:

  • 这是我认为我必须走的路径——问题似乎是我仍然需要在主应用程序目录中拥有 MyCoolDll.dll 的依赖项,因为 .net 不会在加载 MyCoolDll.dll 之前,不要在子文件夹中搜索它们(因此不会先设置程序集解析器)。令我惊讶的是,汉斯的上述建议有效(即使 MyCoolApp.exe 不是托管可执行文件。)
  • 您的非托管 DLL 将在需要程序集解析之前加载。然后您可以自己加载混合模式 DLL,并调用程序集解析设置。对于复杂的场景,你需要它;但是,如果一个简单的配置文件适用于您的情况,那就太好了!
  • 我看到的问题是我的混合模式 dll 依赖于其他一些 .NET 程序集——所以除非我将这些依赖项放在主目录而不是子文件夹中,否则我将无法获取混合模式 dll 中的任何可执行代码,我可以在其中连接程序集解析器。
  • 谢谢,这行得通,但我有一些意见/问题: - 你为什么要对 mscorlib 做一些特别的事情?它似乎无需做任何事情就可以工作 - 使用 AssemblyName 来解析 args->Name 会更好 - 小心返回实际请求的程序集,而不是总是相同的。我忽略了这样做,然后发生了不好的事情:)
  • @Yaurthek 这是工作代码中的一个示例,显示您可以在此调用中选择从何处加载。在我们的例子中,我们想从不同的地方加载这些库。
【解决方案2】:

在这种情况下,CLR 以一种不寻常的方式加载,通过编译器在编译 __declspec(dllexport) 的本机导出时注入的 thunk。这样做很好,只是不是特别快。

CLR 将寻找 .config 文件来初始化主 AppDomain。并且会查找 MyCoolApp.exe.config,无论这根本不是托管可执行文件。您可以使用&lt;probing&gt; element 添加子目录来搜索程序集。

【讨论】:

    【解决方案3】:

    我遇到了这个问题,但还有一个问题 22,因为我的托管 C++ DLL 会立即崩溃,因为它直接引用了它找不到的 .NET DLL。我什至根本无法调用任何 .NET 代码,所以 Ed 的解决方案最初并不适合我。

    诀窍是创建第二个托管 C++ DLL,不引用任何特殊的 .NET DLL。如果没有这些参考,它会很好地加载。在第二个 DLL 中,运行 Ed 的代码来设置程序集解析器。然后像以前一样加载有问题的托管 C++ DLL,它工作正常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-03
      • 2011-02-11
      • 1970-01-01
      相关资源
      最近更新 更多