Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Anyone who ever worked with Windows development before .NET should know how to load DLLs dynamically using LoadLibrary, GetProcAddress and FreeLibrary from the Windows API. However, to my surprise, it's not possible to convert the pointer returned by GetProcAddress to something like a delegate. So, I created a small trick to use legacy plug-ins still using PInvoke. Notice that when I say "legacy", I'm referring to any code not written to .NET, but Win32.

Background

In the far far past, in the days I began to learn C#, I tried to call legacy DLLs within .NET with some code derived from what I used to do using Delphi. When I got tired of trying (even using unsafe) and posted the question on a MS newsgroup, they said that was really not possible. The problem is: I wanted a solution. Now that I have one, I decided to share it.

The trick is far more simple than it sounds, but requires a few more coding steps. First, we need some DLL to work as our plug-in. I will not write any code for that, we will just suppose it has only one method and we can import it with the following code:

[DllImport ("plugin.dll")]
private extern static int SomeMethod(int a, int b);

This way we can imagine any DLL written in any non-.NET language. The problem with this is that we can access the method in only one specific DLL and we want to use an uncertain count of libraries this way.

Using the code

We want to access multiple legacy DLLs dynamically, and we can't convert and call the function pointer returned by GetProcAddress as, for example, a .NET delegate. Although, I still can use the pointer returned by LoadLibrary. So, we could create another legacy DLL imported with this code:

[DllImport ("plugin.dll")]
private extern static int SomeMethod(IntPtr lib, int a, int b);

This is where the trick lies. We have a well known DLL but it isn't the plug-in call yet. Notice the first parameter in this method: this could be anything, but we'll use the return value of the LoadLibrary function. Once we can't call the dynamic function directly, we use another legacy library as a front-end just to call the plug-in. The front-end is exactly the same as the plug-in libraries, but every function has that IntPtr parameter as it's first parameter. It's very important to remember that every method of the plug-in libraries must have a correspondent in this front-end in order to be used.

Now that the trick is explained, it's very simple to create some class to be extended to allow us to load the legacy plug-ins in a more object-oriented fashion.

using System;
using System.Runtime.InteropServices;

public abstract class LegacyPlugin: IDisposable {

   [DllImport ("kernel32.dll")]
   private extern static IntPtr LoadLibrary(string fileName);

   [DllImport ("kernel32.dll")]
   private exten static bool FreeLibrary(IntPtr lib);

   private IntPtr lib = IntPtr.Zero;

   protected LegacyPlugin(string fileName) {
      lib = LoadLibrary(fileName);
   }

   ~LegacyPlugin() {
      Dispose();
   }

   public void Dispose() {
      if(lib == IntPtr.Zero) return;
      FreeLibrary(lib);
      lib = IntPtr.Zero;
   }

   protected IntPtr Library {
      get {
         if(lib == IntPtr.Zero)
            throw new InvalidOperationException("Plug-in library is not loaded");
         return lib;
      }
   }
}

This class can be extended for each different type of legacy plug-in your application uses. All you need to do is declare the DllImports for the functions in the front-end DLL. So, our example plug-in could be accessed using the following class:

using System;

public class SomePlugin: LegacyPlugin {

   // Notice we changed the name of the method because of the public version
   // DLL name search is case-insensitive but C# source code is sensitive
   [DllImport ("plugin.dll")]
   private extern static int someMethod(IntPtr lib, int a, int b);

   public int SomeMethod(int a, int b) {
      return someMethod(Library, a, b);
   }
}

Points of Interest

I hope to be clear that we're calling the front-end library (which must be created in a legacy language like Visual C++ or Delphi) and that this front-end still has to call GetProcAddress, convert its return value to a function, call it and return the return value of this function.

Another way to accomplish this is to create, static or dynamically, a class for each plug-in library we have to load. This implies in an obvious overhead for the application, not to mention it's more time-consuming.

Still, I'm not sure if it's possible to do the pointer conversion (from GetProcAddress) to a delegate using Delphi 8, once they tried to keep compatibility with previous versions. My first supposition is no. If anyone has tested, please let us know.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralEmit?
mscholz
22:21 21 Jun '04  
Hi,

if you want to load a dll, you normaly use the DLLImportAttribute. You can emit this attribute at runtime to load a dll. Do something like this:

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

public class DynamicBeeper
{
public static void Main()
{
DynamicPInvoke("user32.dll", "MessageBeep", typeof(bool),
new Type[]{ typeof(uint) }, new Object[]{ (uint)0 });
}

public static object DynamicPInvoke(string dll, string entryPoint,
Type returnType, Type [] parameterTypes, object [] parameterValues)
{
// Create a dynamic assembly and a dynamic module
AssemblyName asmName = new AssemblyName();
asmName.Name = "tempAssembly";
AssemblyBuilder dynamicAsm =
AppDomain.CurrentDomain.DefineDynamicAssembly(asmName,
AssemblyBuilderAccess.Run);
ModuleBuilder dynamicMod =
dynamicAsm.DefineDynamicModule("tempModule");

// Dynamically construct a global PInvoke signature
// using the input information
MethodBuilder dynamicMethod = dynamicMod.DefinePInvokeMethod(
entryPoint, dll, MethodAttributes.Static | MethodAttributes.Public
| MethodAttributes.PinvokeImpl, CallingConventions.Standard,
returnType, parameterTypes, CallingConvention.Winapi,
CharSet.Ansi);

// This global method is now complete
dynamicMod.CreateGlobalFunctions();

// Get a MethodInfo for the PInvoke method
MethodInfo mi = dynamicMod.GetMethod(entryPoint);

// Invoke the static method and return whatever it returns
return mi.Invoke(null, parameterValues);
}
}


GeneralRe: Emit?
NeosMatrix
3:58 8 Jul '04  
Hi,

Great bit of code, however will this work with custom dlls or does it have to be system dlls. If custom ones will work, when you specify the dll name do you have to give the absolute path. Also do you know anything about getting the returning values back out from the call.

Thanks

Jason
GeneralRe: Emit?
mscholz
20:31 11 Jul '04  
It works will all DLLs, user and system dlls.
The method signature is:

public static :-DobjectBig Grin DynamicPInvoke(string dll, string entryPoint, :-DType returnTypeBig Grin , Type [] parameterTypes, object [] parameterValues)

The functions returns the return value, you only have to cast it to returnType.

Of cause you can (or) must enhance these method if you want to use custom marshalling.

The dll will be searched in the normal "windows" way, (current directroy, system directory, PATH ....)

GeneralRe: Emit?
Harkos
3:58 15 Jul '04  
I've thought about this solution too, but I think it overloads the application with too many "code emittions" (once everytime I call any method in a plug-in).Frown

The solution I came with calls only the GetProcAddress on every call. This could also be reduced if I load every function pointer (using the singleton pattern is a good idea) an only pass it to the auxiliar DLL to call. Wink

[]'s,
Harkos
---
.NET Develeloper
GeneralRe: Emit?
mscholz
4:29 15 Jul '04  
Save the MethodInfo mi after the first time you call the DLL-Method. (for example in a Hashtable)

The code is just an idea, not a complete solution...still room for improvements Smile

Marco
GeneralRe: Emit?
Harkos
4:49 15 Jul '04  
It works too, but I tried to save the overhead of creating a new method dynamically for every method in every DLL. In the end both solutions will work for any DLL, we just have to evaluate their performance.

[]'s,
Harkos
---
.NET Developer
GeneralRe: Emit?
Fabrizio Carrai
22:53 19 Jan '06  
Great code, but I've a problem with the return value.

I've a very basic DLL that's just returning an 'int'. When invoke with mi.Invoke it always returns '0', instead of the expected value (i.e. '10' in my specific case. I instrumented the DLL so I'm pretty sure it does what it should.

I set 'returnType' to "typeof(int)"

Any idea ? Thanks!

Fabrizio
GeneralRe: Emit?
mscholz
23:40 19 Jan '06  
Some ideas:

- To isolate the problem, try a Win32 Function with the same signature.

- Try to call your dll with a normal static-linked [DllImport] attribut and check the return value.

- Check calling conventions

Good luck.
GeneralRe: Emit?
Fabrizio Carrai
2:51 20 Jan '06  
Hello,
thanks for your fast reply!

"try a Win32...": I tried with the "atoi" function, with the same result:

object o1 = DynamicPInvoke("NTDLL.dll", // DLL name
"atoi", // Entry point
typeof(int), // Return type
new Type[]{ typeof(string) }, // Parameter types
new Object[]{ (string)"123" } // Parameter list
);

The static-link mechanism works (I used it as reference), both for my function and for the "atoi" test above,

What do you mean for "check calling conventions ? In my test functions I use only integer (int Somma(int a, int b) )..

Thanks for any idea...


Fabrizio
GeneralRe: Emit?
walker_network_ranger
0:23 15 Nov '06  
Fabrizio Carrai wrote:
"try a Win32...": I tried with the "atoi" function, with the same result:

object o1 = DynamicPInvoke("NTDLL.dll", // DLL name
"atoi", // Entry point
typeof(int), // Return type
new Type[]{ typeof(string) }, // Parameter types
new Object[]{ (string)"123" } // Parameter list
);


Hello,

I have the same problem as you, Frabritzio (return value is always zero when i expect to be an positive integer value). I tried also your example above (with calling atoi method) and the result is allways zero.
If someone find a solution for this problem please post it here.
Thanks!



Marius
~~~~~~~~~~~~~~
.NET developer

GeneralRe: Emit?
newportgm
1:52 15 Nov '06  
I had the same problem, and solved it with this :-

[DllImport( "kernel32.dll", EntryPoint = "LoadLibrary" )]
static extern IntPtr LoadLibrary( string lpLibFileName );

[DllImport( "kernel32", EntryPoint = "GetProcAddress", SetLastError = true )]
static extern IntPtr GetProcAddress( IntPtr hModule, string lpProcName );

[DllImport( "kernel32", EntryPoint = "FreeLibrary", SetLastError = true )]
static extern bool FreeLibrary( IntPtr hModule );

public delegate int fn( char[] str );

private static int DbifCommand( string p_sCommand )
{
IntPtr hModule = IntPtr.Zero;
IntPtr pfn = IntPtr.Zero;

// Load the library and get a pointer to the function
hModule = LoadLibrary( "whatever.dll");
pfn = GetProcAddress( hModule, "fn" );

fn myfunc = (fn) Marshal.GetDelegateForFunctionPointer( pfn, typeof( fn ) );

int rtn = myfunc( p_sCommand.ToCharArray() );
return rtn;
}
GeneralRe: Emit?
walker_network_ranger
3:47 19 Nov '06  
I'm sure your solution works but only on Framework 2.0 (because GetDelegateForFunctionPointer method is available only with Framework 2.0).
In this moment I need a solution for Framework 1.1.

Thanks anyway for your help!

Marius
~~~~~~~~~~~~~~
.NET developer

AnswerPInvokeStackImbalance
hahnl
5:50 14 Apr '06  
A PInvokeStackImbalance occures with VS 2005 / NET 2.0 inside "return mi.Invoke(null, parameterValues);"!!

Also with this code:
public sealed class DynamicPInvoke
{
private static Hashtable libraries = new Hashtable();
private static AssemblyBuilder builder;

private static ModuleBuilder module;

static DynamicPInvoke()
{
AssemblyName an = new AssemblyName();
an.Name = "DynamicPInvokeAssembly";
builder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
module = builder.DefineDynamicModule("DynamicPInvokeModule");
}

private DynamicPInvoke()
{
}

[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
[ReflectionPermission(SecurityAction.Demand, ReflectionEmit = true)]
public static object InvokeMethod(string library, string method, Type returnType, object[] args)
{
MethodInfo meth = GetMethod(library, method, returnType, args);
return meth.Invoke(null, args);
}

private static MethodInfo GetMethod(string library, string method, Type returnType, object[] args)
{
if (!libraries.ContainsKey(library))
{
libraries[library] = new Hashtable();
}
Hashtable methods = (Hashtable) libraries[library];
StringBuilder sb = new StringBuilder();
sb.Append(method);
sb.Append('(');
for (int i = 0; i < args.Length; i++)
{
if (i > 0)
{
sb.Append(',');
}
sb.Append(args[i].GetType().FullName);
}
sb.Append(')');
string signature = sb.ToString();
if (!methods.ContainsKey(signature))
{
Type[] paramTypes = new Type[args.Length];
for (int i = 0; i < args.Length; i++)
{
paramTypes[i] = args[i].GetType();
}
MethodInfo meth = DefineMethod(library, method, returnType, paramTypes);
methods[signature] = meth;
return meth;
}
return (MethodInfo) methods[signature];
}

private static MethodInfo DefineMethod(string library, string method, Type returnType, Type[] paramTypes)
{
TypeBuilder tb = module.DefineType(Guid.NewGuid().ToString());
tb.DefinePInvokeMethod(method, library, MethodAttributes.PinvokeImpl | MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, returnType, paramTypes, CallingConvention.Cdecl, CharSet.Auto);
Type type = tb.CreateType();
return type.GetMethod(method, BindingFlags.Static | BindingFlags.Public, null, paramTypes, null);
}


The return-code is wrong, like the statment from Fabrizio, but the functionality will work correct (the extern method will be called correctly), except a PInvokeStackImbalance occures in line "return meth.Invoke(null, args);".

Anyone an idea????

AnswerRe: PInvokeStackImbalance
Dieter Buecherl
1:36 19 Jun '07  
I found at http://groups.google.de/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/98de540828e0324e/381d97720718fe91?lnk=st&q=methodinfo+invoke+PInvokeStackImbalance&rnum=1&hl=de#381d97720718fe91
a hint to use:

dynamicMethod.SetImplementationFlags(MethodImplAttributes.PreserveSig);

and voilá it worked...

HTH Didi

GeneralRe: Emit?
bibiboule
4:44 29 Jan '07  
Hi, you example is excellent mscholz but it can not pass with my code:

FileStream f = File.OpenRead("password");
byte[] buffer = new byte[f.Length];
int buf_size = (int)f.Length;
f.Read(buffer, 0, buf_size);
fixed (byte* pbuffer = buffer)
{
Object ok = DynamicPInvoke("my.dll", "Pass", typeof(bool),
new Type[] { typeof(string), typeof(string), typeof(byte*), typeof(int) }, new Object[] { "name", "pass", *pbuffer,buf_size});

if ((bool)ok) Console.WriteLine("ok");
else Console.WriteLine("no!");
Console.ReadLine();

the trouble is in type Byte* , i think because my dll have this unsafe parameter...

Plz help me :/ thanks a lot !
GeneralRe: Emit?
bibiboule
4:58 29 Jan '07  
Hello,

When i use your code with only natif function beep(uint, uint), i can heard the beep but an exception has throw like this:

PInvokeStackImbalance was detected
Message: A call to PInvoke function 'tempModule!::Beep' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

anyone have some response ? thank a lot !


Last Updated 15 Jun 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010