简体   繁体   English

C#DllImport不存在的功能

[英]C# DllImport of non-existent function

We have some C# code which calls unmanaged code from an external DLL. 我们有一些C#代码,可从外部DLL调用非托管代码。 The external DLLs are used as plug-ins, and may be of different versions. 外部DLL用作插件,并且可以具有不同的版本。 The different versions contain a slightly different set of available functions. 不同版本包含的可用功能略有不同。

What happens when we DllImport a non-existent function? 当我们DllImport一个不存在的函数时会发生什么? What happens when we call it? 当我们称呼它会怎样? Can we know if a specific function is available in the Dll before calling it? 我们可以在调用Dll之前知道Dll中是否有特定功能吗?

More specifically, recent versions of the DLL have a function giving us the version. 更具体地说,DLL的最新版本具有为我们提供该版本的功能。 So for these versions, it is easy to know which functions are available. 因此,对于这些版本,很容易知道哪些功能可用。 But we would also need to know if the DLL is of a version older than where this function was introduced. 但是,我们还需要知道DLL是否具有比引入此功能的地方更旧的版本。

The .net runtime will JIT your code on demand. .net运行时将按需JIT您的代码。 This is how you accomplish this. 这就是您完成此操作的方式。

If you rely on lazy instantiation of the code that depends on the DLL function that may or may not be there. 如果您依赖依赖于可能存在或可能不存在DLL函数的代码的惰性实例化。 You can use the GetProcAddress function to check for the function. 您可以使用GetProcAddress函数检查该函数。 This is what we would do if we were writing good old Win32 code. 如果我们正在编写旧的Win32代码,这就是我们要做的。

Here's a simple example from Jon Skeet's article about laziness: 这是乔恩·斯基特(Jon Skeet)关于懒惰的文章中的一个简单示例:

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();
  }
}

The laziness here is inherit in the JITting, it's not really necessary. 懒惰是在JITting中继承的,这并不是必须的。 However, it does allow us to keep a consistent naming convention. 但是,它确实允许我们保持一致的命名约定。

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

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

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

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