从C#调用非托管C ++ dll的方法差异

时间:2019-04-16 18:43:46

标签: c# unmanaged

我正在从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吗? Windows是否可以在我不知情的情况下加载和卸载dll?只要它会在dll中调用函数时自动加载,我就不会在乎。

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

2 个答案:

答案 0 :(得分:2)

我给你答案,但是你似乎已经了解发生了什么事。

  

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

这两种方法之间没有功能上的区别。在第一种方法(如果可能的话,应该使用该方法)中,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框架是您的朋友,让它为您完成繁重的工作,并将精力集中在其余的代码上。

答案 1 :(得分:0)

当您使用DllImport调用时,LoadLibrary最终会被您调用,并且(希望)在EAT中找到了导出的函数。基本上,在第二个示例中,您将执行与c / c ++的typedef + GetModuleHandle和GetProcAddress相同的操作。

我可以考虑使用第二种方法的唯一原因是,如果非托管模块的DllMain在附加到进程后执行代码,则可能要根据情况想对模块何时获得特定的时序控制进行控制加载到您的过程中。