如果有选择,混合模式程序集与单独的互操作DLL的优缺点是什么



当第三方组件同时提供"混合模式汇编"one_answers"单独的interop.dll"版本时,两者的优缺点是什么?

一个很好的例子是System.Data.SQLite.

上面的链接是这样说的:

只有在由于某种原因必须将程序集二进制文件部署到全局程序集缓存的情况下,才应使用[混合模式程序集]包。

但为什么?混合模式程序集在我的项目中似乎工作得很好,没有GAC安装(只是xcopy到应用程序的exe目录)。少一个DLL真是太好了。感觉更整洁。那么缺点是什么呢?

反之亦然,为什么人们会/应该支持两个DLL"本机DLL+互操作DLL"版本?

免责声明:要想得到确切的答案,你必须询问开发团队的人员,但这是我的最佳猜测。

在标准配置中,托管程序集将尝试定位并加载所需的本机DLL。它将在依赖于平台的目录(x86x64)中进行搜索。然后,它将加载在那里找到的DLL,并继续向其抛出P/Invoke互操作

这是与.NET中的本机库进行互操作的一个相当标准的过程-唯一的自定义System.Data.SQLite代码是尝试定位DLL并加载正确版本的代码。其余的是纯P/Invoke。但即便如此,当你与图书馆打交道时,这也是司空见惯的做法。

这种方法的主要优点是库用户可以为AnyCPU平台构建他的项目,并且处理器体系结构将在运行时得到解决-如果在x86或x64上运行,只要两个本地DLL都可用,一切都会按预期工作。图书馆的作者得到的支持请求更少。


让我们将其与混合模式方法进行比较。混合模式DLL有一些缺点,主要的缺点是它必须是特定于平台的。因此,如果您选择这种方法,则必须将您的应用程序绑定到特定的平台。如果您想同时支持x86和x64,则必须构建单独的版本,每个版本都链接到正确版本的System.Data.SQLite.

如果你没有完全正确地理解这一点,那么繁荣。更糟糕的是,如果您在x64开发机器上为AnyCPU平台构建它,乍一看它似乎运行良好,但它会在客户的旧x86机箱上崩溃。必须处理这类问题并不好,使用单独DLL的简单解决方案完全解决了这个问题。

我能想到的另一个缺点是无法从内存加载程序集,但这最多只是一个小的不便,因为这也适用于本机DLL。

至于GAC,我的猜测是,在单独的本机程序集的情况下,搜索它们的代码在某些情况下可能无法找到它们,或者它可能找到不同版本的DLL。System.Data.SQLite中的代码非常努力地查找本机DLL,但混合模式DLL一开始就没有这样的问题,因此不能选择失败。


尽管如此,你说:

感觉更整洁。

让我们仔细看看这个。:)

System.Data.SQLite对混合模式互操作有一种非常不寻常的方法。通常,您会使用C++/CLI来构建混合模式程序集。这使您可以将同一C++/CLI项目中的托管代码和本机代码组合到一个DLL中,并使用所谓的C++互操作来处理从托管/非托管屏障的一端到另一端的调用。这样做的优点是它比P/Invoke更轻、更快,因为它可以避免大部分封送处理。

System.Data.SQLite做了一些不同的事情:它将C#代码构建到netmodule中,然后使用C++链接器将netmodule

有趣的是,在取消C++/CLI的链接时,C#没有直接机制来在同一混合模式程序集中调用本机代码,因为C#最初并不是真正用于混合模式程序集的。因此,来自这个最终程序集的C#代码将简单地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

如果您想了解构建过程,请查看C++MSBuild项目。以下是一些链接器选项,显示网络模块的使用(在<AdditionalDependencies>中):

<Link>
  <AdditionalOptions>$(INTEROP_ASSEMBLY_RESOURCES) %(AdditionalOptions)</AdditionalOptions>
  <AdditionalLibraryDirectories>$(INTEROP_LIBRARY_DIRECTORIES)</AdditionalLibraryDirectories>
  <AdditionalDependencies>$(ProjectDir)..bin$(ConfigurationYear)$(Configuration)ModulebinSystem.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>

最新更新