C# Dll导入不存在的函数

C# DllImport of non-existent function

我们有一些 C# 代码可以从外部 DLL 调用非托管代码。外部 DLL 作为插件使用,可能有不同的版本。不同的版本包含一组略有不同的可用功能。

当我们 DllImport 一个不存在的函数时会发生什么? 当我们调用它时会发生什么? 我们能否在调用之前知道Dll中是否有特定功能?

更具体地说,最新版本的 DLL 具有为我们提供版本的功能。所以对于这些版本,很容易知道有哪些功能可用。但我们还需要知道 DLL 的版本是否早于引入此函数的版本。

.net 运行时将按需 JIT 您的代码。这就是您完成此任务的方式。

如果您依赖依赖于可能存在或不存在的 DLL 函数的代码的惰性实例化。您可以使用 GetProcAddress 函数来检查函数。如果我们正在编写良好的旧 Win32 代码,这就是我们会做的事情。

这是 Jon Skeet article 关于懒惰的一个简单例子:

public sealed class Singleton
{
  [DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)]
  private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

  [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
  private static extern IntPtr GetModuleHandle(string lpModuleName);

  public bool IsQueryFullProcessImageNameSupported { get; private set; }

  public string QueryFullProcessImageName(IntrPtr handle)
  { 
    if (!IsQueryFullProcessImageNameSupported) {
      throw new Exception("Does not compute!");
    }
    int capacity = 1024;
    var sb = new StringBuilder(capacity);
    Nested.QueryFullProcessImageName(handle, 0, sb, ref capacity);
    return sb.ToString(0, capacity);
  }

  private Singleton()
  {
    // You can use the trick suggested by @leppie to check for the method
    // or do it like this. However you need to ensure that the module 
    // is loaded for GetModuleHandle to work, otherwise see LoadLibrary
    IntPtr m = GetModuleHandle("kernel32.dll");
    if (GetProcAddress(m, "QueryFullProcessImageNameW") != IntrPtr.Zero) 
    {
      IsQueryFullProcessImageNameSupported = true;
    }
  }

  public static Singleton Instance { get { return Nested.instance; } }

  private class Nested
  {
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Nested()
    {
      // Code here will only ever run if you access the type.
    }

    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags, [Out]StringBuilder lpExeName, ref int lpdwSize);

    public static readonly Singleton instance = new Singleton();
  }
}

这里的惰性是在JITting中继承的,其实没有必要。但是,它确实允许我们保持一致的命名约定。

在调用该方法之前使用Marshal.Prelink(MethodInfo)检查它是否有效。

如果您尝试调用不存在的函数,将抛出 EntryPointNotFoundException