简体   繁体   English

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

[英]Given the choice, what are the pros/cons of mixed-mode assemblies vs. separate interop DLLs?

When a 3rd-party component is offered in both "mixed-mode assembly" and "separate interop dll" versions, what are the pros and cons of each? 当“混合模式组装”和“单独的互操作dll”版本都提供第三方组件时,每种组件的优缺点是什么?

A good example is System.Data.SQLite . 一个很好的例子是System.Data.SQLite

The above link has this to say: 以上链接有这样的说法:

[Mixed-mode assembly] packages should only be used in cases where the assembly binary must be deployed to the Global Assembly Cache for some reason. 只有在出于某种原因必须将汇编二进制文件部署到全局程序集缓存的情况下,才应使用[混合模式汇编]程序包。

But WHY? 但为什么? The mixed-mode assembly seems to work just fine in my projects, with no GAC installation (just xcopy to the application's exe directory). 混合模式程序集似乎在我的项目中工作得很好,没有安装GAC(只需xcopy到应用程序的exe目录)。 It's nice to have one less DLL. 有一个更少的DLL是很好的。 It feels tidier. 感觉更整洁。 So what are the cons? 那么缺点是什么?

Vice-versa, why would/should one ever favor the two-DLL "native-dll + interop-dll" version? 反之亦然,为什么一个人会赞成这两个DLL“native-dll + interop-dll”版本?

Disclaimer: For a definite answer, you'd have to ask someone from the development team, but here's my best guess. 免责声明:对于一个明确的答案,你必须问开发团队的人,但这是我最好的猜测。

In the standard configuration, the managed assembly will try to locate and load the native DLL it needs. 在标准配置中,托管程序集将尝试查找并加载所需的本机DLL。 It will search in the platform-dependent directories ( x86 or x64 ). 它将在依赖于平台的目录( x86x64 )中进行搜索。 It will then load the DLL it found there and proceed to throw P/Invoke interop at it. 然后它将加载它在那里找到的DLL并继续向它抛出P / Invoke互操作。

This is a fairly standard procedure for interoperating with native libraries in .NET - the only custom System.Data.SQLite code is the one that tries to locate the DLL and loads the right version. 这是与.NET中的本机库进行互操作的一个相当标准的过程 - 唯一的自定义 System.Data.SQLite代码是试图找到DLL并加载正确版本的代码。 The rest is plain P/Invoke. 其余的是普通的P / Invoke。 But even this is commonplace practice when you deal with libraries. 但是,当你处理库时,这甚至是常见的做法。

The major advantage of this approach is that the library user can build his project for the AnyCPU platform, and the processor architecture will be resolved at runtime - everything will work as expected, should you run on x86 or x64, provided both native DLLs are available. 这种方法的主要优点是库用户可以为AnyCPU平台构建他的项目,并且处理器体系结构将在运行时解析 - 如果你在x86或x64上运行,一切都将按预期工作,前提是两个本机DLL都可用。 And the library author gets less support requests. 并且库作者获得的支持请求较少。


Let's compare it with the mixed-mode approach. 让我们将它与混合模式方法进行比较。 A mixed-mode DLL has a few disadvantages, the major one being it must be platform-specific. 混合模式DLL有一些缺点,主要的缺点是它必须是特定于平台的。 So if you choose this approach, you'll have to tie your application to a particular platform. 因此,如果您选择此方法,则必须将应用程序绑定到特定平台。 If you want to support both x86 and x64, you'll have to build separate versions, each one linking to the correct version of System.Data.SQLite. 如果要同时支持x86和x64,则必须构建单独的版本,每个版本都链接到正确版本的System.Data.SQLite。

If you don't get this perfectly right, then boom . 如果你没有完全正确,那么繁荣 Worse, if you build it for the AnyCPU platform on your x64 development machine, it will seem to work alright at first glance, but it will implode on your customer's old x86 box. 更糟糕的是,如果你在x64开发机器上为AnyCPU平台构建它, 乍一看似乎没问题 ,但它会在你客户的旧x86机箱上内爆。 Having to deal with this kind of issues isn't nice, and the simple solution of using separate DLLs solves the problem entirely. 必须处理这类问题并不好,使用单独的DLL的简单解决方案完全解决了这个问题。

The other disadvantage I can think of is the inability to load the assembly from memory, but this should be at most a minor inconvenience, since this applies to native DLLs too. 我能想到的另一个缺点是无法从内存加载程序集,但这最多只是一个小小的不便,因为这也适用于本机DLL。

As for the GAC, my guess would be that in the case of separate native assemblies, the code which searches for them could fail to locate them in some cases, or it could locate a different version of the DLL. 至于GAC,我的猜测是,在单独的本机程序集的情况下,搜索它们的代码在某些情况下可能无法找到它们,或者它可能找到不同版本的DLL。 The code in System.Data.SQLite tries really hard to locate the native DLL, but there's no such problem with the mixed-mode DLL in the first place, so failure is not an option. System.Data.SQLite中的代码尝试很难找到本机DLL,但首先没有混合模式DLL的问题,所以失败不是一个选项。


Nevertheless, you say: 不过,你说:

It feels tidier. 感觉更整洁。

Let's take a closer look at this. 让我们仔细看看这个。 :) :)

System.Data.SQLite has quite an unusual approach to mixed-mode interop. System.Data.SQLite对混合模式互操作有一种非同寻常的方法。 Usually, you'd use C++/CLI to build a mixed-mode assembly. 通常,您使用C ++ / CLI构建混合模式程序集。 This lets you combine managed and native code from the same C++/CLI project into a single DLL and uses the so-called C++ Interop to deal with calls from the one to the other side of the managed/unmanaged barrier. 这使您可以将来自同一 C ++ / CLI项目的托管代码和本机代码组合到一个DLL中,并使用所谓的C ++ Interop来处理从托管/非托管屏障的一端到另一端的调用。 The advantage of this is that it's lighter and faster than P/Invoke, as it can avoid most of the marshaling. 这样做的好处是它比P / Invoke更轻更快,因为它可以避免大部分编组。

System.Data.SQLite does something different: it builds its C# code into a netmodule , and then uses the C++ linker to link the netmodule with the native SQLite code. System.Data.SQLite做了一些不同的事情:它将C#代码构建到netmodule中 ,然后使用C ++链接器netmodule与本机SQLite代码链接起来。 This results in a mixed-mode assembly. 这导致混合模式组装。

The interesting thing is that, unline C++/CLI, C# has no direct mechanism to invoke native code in the same mixed-mode assembly, as C# wasn't really intended to be used in mixed-mode assemblies in the first place. 有趣的是,在使用C ++ / CLI时,C#没有直接机制来调用同一混合模式程序集中的本机代码,因为C#并不是真正打算在混合模式程序集中使用。 So, the C# code from this final assembly will simply P/Invoke itself . 因此,来自此最终程序集的C#代码将简单地P / Invoke自身 Yes, you read that right. 是的,你看的没错。 Does this still feel as tidy? 这还是觉得整洁吗? Nevertheless, it's a clever hack. 不过,这是一个聪明的黑客。 :) :)

Take a look at the code in UnsafeNativeMethods.cs : 看一下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

If you'd like to see the build process, take a look at the C++ MSBuild projects. 如果您想查看构建过程,请查看C ++ MSBuild项目。 Here are some linker options showing the use of netmodules (in <AdditionalDependencies> ): 以下是一些链接器选项,显示了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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM