Динамическая загрузка смешанного режима C ++ / CLI .dll (и зависимостей) из неуправляемого c ++

У меня есть управляемая сборка C ++, которую я загружаю динамически в неуправляемом приложении C ++ с помощью стандартного вызова LoadLibrary (). Управляемая сборка C ++ зависит от еще нескольких управляемых сборок (C #). Все работало нормально, пока я не переместил все управляемые сборки в подкаталог неуправляемого приложения. Проиллюстрировать:

  • Управляемая библиотека C ++ .dll (MyCoolDll.dll)

    • Dependent on DotNetDll1.dll
    • Зависит от DotNetDll2.dll
  • Неуправляемое приложение C ++ (MyCoolApp.exe)

    • Loads MyCoolDll.dll via LoadLibrary("MyCoolDll.dll")

Это работало нормально, пока я не переместил MyCoolDll.dll, DotNetDll1.dll и DotNetDll2.dll в / someSubDirectory (код в MyCoolApp.exe был обновлен до LoadLibrary («someSubDirectory / MyCooldll.dll»)

Я предполагаю, что когда MyCoolDll.dll загружен, он пытается найти DotNetDll1.dll и DotNetDll2.dll в рабочем каталоге, а не в каталоге, в котором он находится.

Как я могу сказать MyCoolDll.dll, что его зависимости находятся в подкаталоге? Это библиотека, работающая внутри неуправляемого приложения, поэтому я не думаю, что могу указать это в app.config или в чем-то еще?


person Jordan0Day    schedule 10.08.2011    source источник
comment
Вау, Ганс, это сработало! Я был очень сомневается, поскольку MyCoolApp.exe - это просто старое приложение Win32 (не .NET), поэтому я решил, что добавление файла конфигурации приложения для него не поможет. Спасибо! Вы хотите написать это как ответ вместо комментария, и я отмечу его как принятый?   -  person Jordan0Day    schedule 11.08.2011


Ответы (2)


В этом сценарии среда CLR загружается необычным образом через преобразователь, который компилятор вводит при компиляции собственного экспорта для __declspec (dllexport). Это нормально, только не очень быстро.

CLR отправится на поиски файла .config для инициализации основного домена приложения. И будет искать MyCoolApp.exe.config, даже если это вообще не управляемый исполняемый файл. Вы можете использовать элемент <probing> , чтобы добавить подкаталоги для поиска сборок.

person Hans Passant    schedule 11.08.2011

Я думаю, что вам нужен специальный преобразователь сборок. Мне пришлось использовать один, чтобы сделать то, что, как я думаю, вы пытаетесь сделать - я хотел найти некоторые библиотеки 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;
}
person Ed Bayiates    schedule 10.08.2011
comment
Это путь, по которому, как я полагал, мне придется спуститься - проблема, казалось, заключалась в том, что мне все еще требовалось иметь зависимости MyCoolDll.dll в основном каталоге приложения, поскольку .net не будет искать их в подпапке даже раньше. загрузка MyCoolDll.dll (чтобы преобразователь сборок не настраивался первым). К моему удивлению, предложение Ханса, приведенное выше, работает (хотя MyCoolApp.exe не является управляемым исполняемым файлом). - person Jordan0Day; 11.08.2011
comment
Ваша неуправляемая DLL загрузится до того, как потребуется разрешение сборки. Затем вы можете загрузить DLL смешанного режима самостоятельно и вызвать настройку разрешения сборки. Для сложных сценариев вам это понадобится; однако, если в вашем случае подойдет простой файл конфигурации, отлично! - person Ed Bayiates; 12.08.2011
comment
Проблема, которую я видел, заключалась в том, что моя dll смешанного режима зависела от нескольких других сборок .NET, поэтому, если я не поместил эти зависимости в основной каталог, а не во вложенную папку, я не смог бы получить какой-либо исполняемый код в моем смешанный режим dll, где я мог подключить преобразователь сборки. - person Jordan0Day; 12.08.2011
comment
Спасибо, это работает, но у меня есть несколько замечаний / вопросов: - Почему вы делаете что-то особенное с mscorlib? Кажется, он работает, ничего не делая - использование AssemblyName для анализа аргументов ›Name было бы лучше. Будьте осторожны, чтобы вернуть фактически запрошенную сборку, а не всегда ту же самую. Я этого не сделал, и в результате последовали неприятности :) - person Melvyn; 15.08.2017
comment
@Yaurthek Это был пример из рабочего кода, чтобы показать, что вы можете выбрать, откуда загружать в этом вызове. В нашем случае мы хотели загрузить эти библиотеки из другого места. - person Ed Bayiates; 16.08.2017
comment
@EdBayiates Почему вы используете try catch для распознавателя сборки вместо того, чтобы указать, существует ли файл? - person Code Name Jack; 10.12.2018
comment
@CodeNameJack может возникнуть множество ошибок (например, не удается открыть, неправильный формат бит, выгружаемый и т. Д.), И один catch аккуратно обрабатывает все из них. Несуществующий файл - это всего лишь одна из возможностей, и если вы посмотрите, как работает поиск по кодовой базе, загрузка может по-прежнему работать, даже если файл, который вы ожидали, не существует. Все ошибки обрабатываются одинаково (возвращается nullptr), поэтому добавление множества других условий IF не приносит особого преимущества. Это будет просто беспорядок в коде. Хотя исключения не очень эффективны, загрузка DLL - это настолько долгая операция, что производительность здесь не имеет значения. - person Ed Bayiates; 10.12.2018
comment
Я согласен с тем, что вы говорите, но в ситуациях, когда есть 3-4 папки для поиска файлов, он будет продолжать выдавать исключения, по крайней мере, вначале. Кстати, спасибо за объяснение. - person Code Name Jack; 10.12.2018