当在"混合模式组装"中提供第三方组件时和"分离互操作dll"版本,各自的优缺点是什么?
一个很好的例子是System.Data.SQLite。
以上链接可以这样说:
[混合模式汇编]软件包仅应在必须将汇编二进制文件部署到全局程序集缓存的情况下使用。
但是为什么?混合模式程序集似乎在我的项目中工作得很好,没有安装GAC(只需xcopy到应用程序的exe目录)。拥有一个更少的DLL是件好事。它感觉更整洁。那有什么缺点?
反之亦然,为什么/应该支持这两个DLL" native-dll + interop-dll"版本
答案 0 :(得分:5)
免责声明:要获得明确的答案,您必须向开发团队的某个人询问,但这是我最好的猜测。
在标准配置中,托管程序集将尝试查找并加载所需的本机DLL。它将搜索与平台相关的目录(x86
或x64
)。然后它将加载它在那里找到的DLL并继续向它抛出P / Invoke互操作。
这是与.NET中的本机库进行互操作的一个相当标准的过程 - 唯一的自定义 System.Data.SQLite代码是试图找到DLL并加载正确版本的代码。其余的是普通的P / Invoke。但是,当你处理图书馆时,即使这是常见的做法。
这种方法的主要优势是库用户可以为 AnyCPU 平台构建他的项目,并且处理器架构将在运行时解决 - 一切都会工作正如预期的那样,如果两个本机DLL都可用,您应该在x86或x64上运行吗?图书馆作者获得的支持请求较少。
让我们将其与混合模式方法进行比较。混合模式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++ Interop来处理从一个到另一个的调用。管理/非管理障碍。这样做的好处是它比P / Invoke更轻更快,因为它可以避免大部分的编组。
System.Data.SQLite做了不同的事情:它将C#代码构建到 netmodule ,然后uses the C++ linker将 netmodule 与原生SQLite链接起来码。这导致混合模式组装。
有趣的是,在取消使用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项目。以下是一些链接器选项,显示了netmodules的使用(在<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>