Учитывая выбор, каковы плюсы и минусы сборки смешанного режима по сравнению с отдельными DLL-взаимодействиями?

Когда сторонний компонент предлагается как в версиях "смешанного режима сборки", так и в "отдельных версиях interop dll", каковы плюсы и минусы каждого из них?

Хорошим примером является System.Data.SQLite.

В приведенной выше ссылке есть следующее:

Пакеты

[Смешанный режим] должны использоваться только в тех случаях, когда сборная двоичная информация должна быть развернута в глобальном кэше сборок по какой-либо причине.

Но ПОЧЕМУ? Сборка смешанного режима, похоже, очень хорошо работает в моих проектах без установки GAC (просто xcopy в каталог exe приложения). Приятно иметь еще одну DLL. Он чувствует себя более аккуратным. Итак, какие же недостатки?

И наоборот, зачем/нужно когда-либо одобрять версию DLL-native-dll + interop-dll?

Ответы

Ответ 1

Отказ от ответственности:. Для определенного ответа вам нужно будет спросить кого-то из команды разработчиков, но здесь мое лучшее предположение.

В стандартной конфигурации управляемая сборка попытается найти и загрузить нужную DLL. Он будет искать в зависимых от платформы каталогах (x86 или x64). Затем он загрузит DLL, найденную там, и перейдет на него. P/Invoke взаимодействует с ним.

Это довольно стандартная процедура для взаимодействия с родными библиотеками в .NET. Единственный пользовательский код System.Data.SQLite - это тот, который пытается найти DLL и загружает нужную версию. Остальное - это просто P/Invoke. Но даже это обычная практика, когда вы имеете дело с библиотеками.

Преимущество этого сильного основного заключается в том, что пользователь библиотеки может построить свой проект для платформы AnyCPU, а архитектура процессора будет разрешена во время выполнения - все будет работать так, как ожидалось, при запуске x86 или x64, при условии, что доступны как собственные DLL. И автор библиотеки получает меньше запросов на поддержку.


Давайте сравним его с смешанным режимом. В смешанной библиотеке DLL есть несколько недостатков, главное, что она должна быть специфичной для платформы. Поэтому, если вы выберете такой подход, вам придется привязать ваше приложение к определенной платформе. Если вы хотите поддерживать как x86, так и x64, вам придется создавать отдельные версии, каждая из которых ссылается на правильную версию System.Data.SQLite.

Если вы не получите это совершенно правильно, тогда бум. Хуже того, если вы построите его для платформы AnyCPU на вашей машине разработки x64, она, похоже, сработает хорошо на первый взгляд, но она будет взорваться на вашем старом x86 вашего клиента. Необходимость иметь дело с такими проблемами не так, и простое решение использования отдельных DLL полностью решает проблему.

Другим недостатком, о котором я могу думать, является невозможность загрузить сборку из памяти, но это должно быть, по крайней мере, небольшим неудобством, поскольку это относится и к родным DLL.

Что касается GAC, я бы предположил, что в случае отдельных собственных сборок код, который их ищет, не сможет найти их в некоторых случаях или может найти другую версию DLL. Код в System.Data.SQLite действительно пытается найти локальную DLL, но в первую очередь проблем с DLL с смешанными режимами нет, поэтому сбой не является вариантом.


Тем не менее вы говорите:

Он чувствует себя более аккуратным.

Давайте поближе рассмотрим это.:)

System.Data.SQLite имеет довольно необычный подход к смешанному взаимодействию. Обычно вы должны использовать С++/CLI для сборки смешанного режима. Это позволяет объединить управляемый и собственный код из одного и того же проекта С++/CLI в одну DLL и использовать так называемый С++ Interop для обработки вызовов от одной до другой стороны управляемого/неуправляемого барьера. Преимущество этого заключается в том, что он легче и быстрее, чем P/Invoke, так как он может избежать большей части маршалинга.

System.Data.SQLite делает что-то другое: он строит свой код С# в netmodule, а затем использует компоновщик С++ для связывания сетевого модуля с собственным кодом SQLite. Это приводит к сборке в смешанном режиме.

Интересно, что unline С++/CLI, С# не имеет прямого механизма для вызова собственного кода в той же сборке в смешанном режиме, поскольку С# на самом деле не предназначался для использования в сборках смешанного режима в первую очередь, Таким образом, код С# из этой окончательной сборки будет просто P/Invoke. Да, вы это правильно поняли. Это все еще кажется таким же аккуратным? Тем не менее, это умный рубить.:)

Взгляните на код в UnsafeNativeMethods.cs:

#if PLATFORM_COMPACTFRAMEWORK
    //
    // NOTE: On the .NET Compact Framework, the native interop assembly must
    //       be used because it provides several workarounds to .NET Compact
    //       Framework limitations important for proper operation of the core
    //       System.Data.SQLite functionality (e.g. being able to bind
    //       parameters and handle column values of types Int64 and Double).
    //
    internal const string SQLITE_DLL = "SQLite.Interop.099.dll";
#elif SQLITE_STANDARD
    //
    // NOTE: Otherwise, if the standard SQLite library is enabled, use it.
    //
    internal const string SQLITE_DLL = "sqlite3";
#elif USE_INTEROP_DLL
      //
    // NOTE: Otherwise, if the native SQLite interop assembly is enabled,
    //       use it.
    //
    internal const string SQLITE_DLL = "SQLite.Interop.dll";
#else
    //
    // NOTE: Finally, assume that the mixed-mode assembly is being used.
    //
    internal const string SQLITE_DLL = "System.Data.SQLite.dll";
#endif

Если вы хотите увидеть процесс сборки, взгляните на проекты С++ MSBuild. Вот некоторые варианты компоновщика, показывающие использование сетевых модулей (в <AdditionalDependencies>):

<Link>
  <AdditionalOptions>$(INTEROP_ASSEMBLY_RESOURCES) %(AdditionalOptions)</AdditionalOptions>
  <AdditionalLibraryDirectories>$(INTEROP_LIBRARY_DIRECTORIES)</AdditionalLibraryDirectories>
  <AdditionalDependencies>$(ProjectDir)..\bin\$(ConfigurationYear)\$(Configuration)Module\bin\System.Data.SQLite.netmodule $(INTEROP_LIBRARY_DEPENDENCIES);%(AdditionalDependencies)</AdditionalDependencies>
  <Version>$(INTEROP_LINKER_VERSION)</Version>
  <GenerateDebugInformation>true</GenerateDebugInformation>
  <GenerateMapFile>true</GenerateMapFile>
  <MapExports>true</MapExports>
  <SubSystem>Windows</SubSystem>
  <OptimizeReferences>true</OptimizeReferences>
  <EnableCOMDATFolding>true</EnableCOMDATFolding>
  <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
  <TargetMachine>MachineX64</TargetMachine>
  <CLRUnmanagedCodeCheck>true</CLRUnmanagedCodeCheck>
  <KeyFile>$(INTEROP_KEY_FILE)</KeyFile>
  <DelaySign>true</DelaySign>
</Link>