【问题标题】:Differences in methods to call unmanaged C++ dll from C#从 C# 调用非托管 C++ dll 的方法的差异
【发布时间】:2019-04-17 03:15:26
【问题描述】:

我正在从 C# 调用一些非托管 C 函数(在外部 dll 中)。我有 2 种不同的方法可以做到这一点,我不确定 2 之间的区别(除了代码量)

方法#1

[DllImport("PComm32.dll",CallingConvention=CallingConvention.StdCall, EntryPoint ="PmacSelect")]
public static extern int PmacSelect(IntPtr intPtr);

int device = PmacSelect(IntPrt.Zero);

方法#2

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int PmacSelect(IntPrt intptr);

[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);

[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);

public PmacSelect PmacSelectFunction;


private IntPtr pDll = LoadLibrary("PComm32");
IntPtr pAddressOfFunctionToCall = GetProcAddress(pDll, "PmacSelect"); //find the function in the loaded pcomm32 dll

PmacSelectFunction = (PmacSelect)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall,type(PmacSelect));

int device = PmacSelectFunction(IntPrt.Zero);

这两种方法都有效,并调用位于 PComm32.dll 文件中的 PmacSelect 函数。 我的问题是这两种方法之间的功能差异是什么? 方法#1 必须依赖 Windows 在后台根据需要为我管理 DLL? Windows 可以在我不知情的情况下加载和卸载 dll 吗?我真的不在乎它是否这样做,只要它在我调用 dll 中的函数时自动加载。

方法 #2 当我调用 LoadLibrary 时,DLL 被显式加载。库在我释放之前是否保留在内存中?

【问题讨论】:

标签: c# unmanaged


【解决方案1】:

我会给你答案,但你似乎已经明白发生了什么。

我的问题是这两种方法之间的功能差异是什么?

这两种方法在功能上没有区别。在第一种方法中(如果可能,您应该使用它),DotNet 框架正在为您处理一切。在后台,它正在执行您手动执行的操作:调用LoadLibraryGetProcAddress,在某些时候调用FreeLibrary。这些是在 DLL 中调用函数的步骤。

方法#1 必须依赖windows 根据需要在后台为我管理DLL? windows可以在我不知情的情况下加载和卸载dll吗?

是的,这是完全正确的,尽管我不会在你不知情的情况下这么说。当您写 [DllImport("PComm32.dll"...)] 时,您是在告诉它这样做。

方法 #2 当我调用 LoadLibrary 时,DLL 被显式加载。库在我释放之前是否保留在内存中?

再次,是的,您了解正在发生的事情。


由于您似乎回答了自己的问题,而我只是确认了您的回答,因此请告诉您为什么应该(几乎)始终使用 #1:

  1. 同样有效
  2. 更易于使用,更易于阅读/维护
  3. 艰苦奋斗没有任何价值(#2)

我只能想到一个你会想要使用第二种方法的原因:如果你需要能够在不退出应用程序的情况下使用更新的版本或其他东西即时替换 DLL,那么您可能希望在卸载 DLL 时进行细粒度控制(以便可以替换文件)。

除非在非常特殊的情况下,否则这不太可能成为要求。

底线:如果您使用 C#,您接受这样一个事实,即您将控制权交给框架以换取能够专注于工作,而不必担心 DLL 或内存管理之类的事情。 DotNet 框架是您的朋友,让它为您完成繁重的工作,并将您的精力集中在其余代码上。

【讨论】:

    【解决方案2】:

    当您使用 DllImport 进行 pinvoke 时,LoadLibrary 最终会为您调用,并且(希望)可以在 EAT 中找到导出的函数。基本上,在您的第二个示例中,您正在做与 c/c++ 的 typedef + GetModuleHandle & GetProcAddress 相同的事情。

    我认为使用第二种方法的唯一原因是,如果您的非托管模块的 DllMain 在附加到进程时执行代码,您可能会根据场景希望对该模块何时获得特定的时序控制加载到您的进程中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-17
      • 1970-01-01
      • 2013-01-07
      • 2013-03-24
      • 1970-01-01
      • 1970-01-01
      • 2016-12-13
      • 2011-03-04
      相关资源
      最近更新 更多