matli
matli

Reputation: 28580

C# DllImport of non-existent function

We have some C# code which calls unmanaged code from an external DLL. The external DLLs are used as plug-ins, and may be of different versions. The different versions contain a slightly different set of available functions.

What happens when we DllImport a non-existent function? What happens when we call it? Can we know if a specific function is available in the Dll before calling it?

More specifically, recent versions of the DLL have a function giving us the version. 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.

Upvotes: 3

Views: 1167

Answers (3)

John Leidegren
John Leidegren

Reputation: 60987

The .net runtime will JIT your code on demand. 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. You can use the GetProcAddress function to check for the function. This is what we would do if we were writing good old Win32 code.

Here's a simple example from Jon Skeet's article about laziness:

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. However, it does allow us to keep a consistent naming convention.

Upvotes: 1

György Kőszeg
György Kőszeg

Reputation: 18013

If you try to call a non-existing function, an EntryPointNotFoundException will be thrown.

Upvotes: 2

leppie
leppie

Reputation: 117220

Use Marshal.Prelink(MethodInfo) to check if it works before calling the method.

Upvotes: 2

Related Questions