如果可以选择,混合模式程序集与单独的互操作 DLL 相比有何优缺点?

2024-01-23

当第 3 方组件以“混合模式程序集”和“单独的互操作 DLL”版本提供时,各自的优缺点是什么?

一个很好的例子是系统.数据.SQLite https://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki.

上面的链接有这样一句话:

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

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

反之亦然,为什么人们会/应该偏爱双 DLL“native-dll + interop-dll”版本呢?


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

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

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

The major这种方法的优点是库用户可以为AnyCPU平台,并且处理器架构将在运行时解析 - 如果您在 x86 或 x64 上运行,一切都会按预期工作,前提是两个本机 DLL 都可用。而且库作者得到的支持请求也更少。


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

如果你没有完全正确地理解这一点,那么boom。更糟糕的是,如果你构建它是为了AnyCPUx64 开发机器上的平台,它将seem乍一看工作正常,但它会在客户的旧 x86 机器上崩溃。必须处理此类问题并不好,使用单独的 DLL 的简单解决方案可以完全解决该问题。

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

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


尽管如此,你还是说:

It feels tidier.

让我们仔细看看这一点。 :)

System.Data.SQLite 有相当多的unusual混合模式互操作方法。通常,您会使用 C++/CLI 来构建混合模式程序集。这使您可以将托管代码和本机代码结合起来sameC++/CLI 项目到单个 DLL 中并使用所谓的C++ 互操作 https://msdn.microsoft.com/en-us/library/ky8kkddw.aspx处理从托管/非托管屏障的一侧到另一侧的调用。这样做的优点是它比 P/Invoke 更轻、更快,因为它可以避免大部分编组。

System.Data.SQLite 做了一些不同的事情:它将其 C# 代码构建成网络模块, 进而使用 C++ 链接器 https://msdn.microsoft.com/en-us/library/k669k83h.aspx链接网络模块与本机 SQLite 代码。这会产生混合模式装配。

有趣的是,unline C++/CLI,C# 没有direct在同一混合模式程序集中调用本机代码的机制,因为 C# 最初并不打算在混合模式程序集中使用。因此,最终程序集中的 C# 代码将简单地P/调用自身。是的,你没有看错。这仍然感觉整洁吗?尽管如此,这是一个聪明的技巧。 :)

看一下中的代码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)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>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果可以选择,混合模式程序集与单独的互操作 DLL 相比有何优缺点? 的相关文章

  • WinApi:获取 COM 表单的控件名称

    我想用 Net 框架替换我当前的 UI 自动化工具 QTP 我需要测试 VB6 COM 应用程序 框架的基础之一是使用表单名称 到目前为止 我未能找到使用 Win API 获取这些数据的方法 该解决方案只有一个约束 即该解决方案必须依赖 N
  • 如何正确复制/克隆结构?我应该使用一个类吗?

    假设我有那个 Structure myStruct Public myPoint As Point Public myBool As Boolean End Structure 如何复制 克隆该结构 我现在解决了这个问题 我使用的代码示例
  • log4net 仅在调用 XmlConfigurator.Configure() 时起作用

    我明白那个this https stackoverflow com questions 445976 log4net config in external file does not work 1479343 1479343 questio
  • 为什么 IsAssignableFrom() 不适用于 int 和 double?

    这是错误的 typeof double IsAssignableFrom typeof int 这是错误的 typeof int IsAssignableFrom typeof double 但这有效 double a 1 0 int b
  • 为什么自定义类型变量不保存值MVC

    我正在尝试使用 MVC 5 构建一个网站 我有一个包含 3 个变量的控制器类 public class WorkerController Controller public ViewModel viewModel new ViewModel
  • 在调试模式下单步执行时跳过方法的属性

    是否有一个属性可以在方法上使用 以便在调试模式下单步执行某些代码时 调试器保持在方法的外部 DebuggerStepThrough docs https learn microsoft com en us dotnet api system
  • System.Drawing.Image.Save 抛出ExternalException:GDI 中发生一般错误

    我有一个函数 它需要一个位图 复制它的一部分并将其保存为 8bpp tiff 结果图像的文件名是唯一的并且文件不存在 程序有权写入目标文件夹 void CropImage Bitmap map Bitmap croped new Bitma
  • 如何动态加载包含非托管代码的原始程序集?(绕过“无法验证的代码失败策略检查”异常)

    我将举一个使用的例子系统 Data SQLite DLL http sqlite phxsoftware com 这是一个包含非托管代码的混合程序集 如果我执行这个 var assembly Assembly LoadFrom System
  • MySql 最后插入 ID,连接器 .net

    我正在使用 MySql Connector net 我需要获取最后一个查询生成的插入 id 现在 我假设返回值是MySqlHelper ExecuteNonQuery应该是最后一个插入id 但它只返回1 我正在使用的代码是 int inse
  • 在 DataGridView 中隐藏行非常慢

    我在 Winforms 应用程序中有一个 DataGridView 大约有 1000 行 未绑定 和 50 列 隐藏一列需要整整 2 秒 当我想隐藏大约一半的行时 这就成为一个问题 private void ShowRows string
  • DotNET 应用程序中的 GDI 句柄

    我的纯 DotNET 库作为非托管桌面应用程序中的插件运行 我收到了稳定的 虽然低 崩溃报告流 这些报告似乎表明 GDI 句柄存在问题 错误消息中的字体等 恢复为系统字体 各种控件的显示崩溃 不久后发生大规模崩溃 我的窗体几乎没有控件 但我
  • Sitecore - 隐藏功能区中的按钮

    我为特定内容项创建了上下文功能区 我有两个按钮 可以将项目 升级 或 降级 到某一类别 该部分只能有一个 是否可以根据某种隐藏代码中的内容状态隐藏其中一个按钮 我了解如何链接到 Click 事件 但我想知道是否有某种加载事件可供自定义功能区
  • 找不到 Microsoft.Office.Interop Visual Studio

    我正在开发一个使用 C 发送电子邮件的应用程序 该应用程序将能够使用邮件模板等 问题是我无法找到任何 Office Interop 引用 这意味着我无法使用 Outlook 我的计算机上安装了 Office 但我也尝试从此链接安装 PIAh
  • C# - 方法必须有返回类型

    我在调用 C 中的方法时遇到问题 不断收到消息 方法 计算 必须有返回类型 using System Diagnostics namespace WindowsFormsApplication1 public partial class F
  • “你好世界!!”在 .NET 4 中生成 3500 个页面错误

    我正在运行 Windows Vista 和 Visual Studio 2010 使用 NET 4 2 GB RAM 和大约 800 MB 可用空间 我创建了一个 Windows 窗体应用程序 但没有向其中添加任何代码 只需在发布模式下编译
  • 使用 SQLITE 按最近的纬度和经度坐标排序

    我必须获得一个 SQLite SQL 语句 以便在给定初始位置的情况下按最近的纬度和经度坐标进行排序 这是我在 sqlite 数据库中的表的例句 SELECT id name lat lng FROM items EXAMPLE RESUL
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 使用.Net/C# 计算集合的频率分布

    是否有一种快速 简单的方法来使用 Linq 或其他方式计算 Net 集合的频率分布 例如 任意长的 List 包含许多重复项 遍历列表并计算 跟踪重复次数的巧妙方法是什么 查找列表中重复项的最简单方法是将其分组 如下所示 var dups
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器

随机推荐