Click here to Skip to main content
6,305,776 members and growing! (17,645 online)
Email Password   helpLost your password?
Languages » C# » PInvoke     Advanced License: The Code Project Open License (CPOL)

Using legacy plug-ins with .NET

By Harkos

Anyone who ever worked with Windows development before .NET should know how to load DLLs dynamically. However, it's not possible to convert function pointers to something like a delegate. So, I created a small trick to use legacy plug-ins still using PInvoke.
C#, Windows, .NET 1.0, .NET 1.1, Visual Studio, Dev
Posted:15 Jun 2004
Views:44,113
Bookmarked:22 times
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
11 votes for this article.
Popularity: 3.42 Rating: 3.29 out of 5
2 votes, 18.2%
1

2
2 votes, 18.2%
3
5 votes, 45.5%
4
2 votes, 18.2%
5

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Harkos


Member
I'm that strange type, who likes to code (C# at all, but with a little bit of Delphi nowadays) and hates to use a database (I'd rather code it, instead).

Although I don't place any form of restriction upon using the codes I provide, I would appreciate to be mentioned as it's author in any projects using them.
Occupation: Architect
Location: Brazil Brazil

Other popular C# articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 16 of 16 (Total in Forum: 16) (Refresh)FirstPrevNext
GeneralEmit? Pinmembermscholz22:21 21 Jun '04  
GeneralRe: Emit? PinmemberNeosMatrix3:58 8 Jul '04  
GeneralRe: Emit? Pinmembermscholz20:31 11 Jul '04  
GeneralRe: Emit? PinmemberHarkos3:58 15 Jul '04  
GeneralRe: Emit? Pinmembermscholz4:29 15 Jul '04  
GeneralRe: Emit? PinmemberHarkos4:49 15 Jul '04  
GeneralRe: Emit? PinmemberFabrizio Carrai22:53 19 Jan '06  
GeneralRe: Emit? Pinmembermscholz23:40 19 Jan '06  
GeneralRe: Emit? PinmemberFabrizio Carrai2:51 20 Jan '06  
GeneralRe: Emit? Pinmemberwalker_network_ranger0:23 15 Nov '06  
GeneralRe: Emit? Pinmembernewportgm1:52 15 Nov '06  
GeneralRe: Emit? Pinmemberwalker_network_ranger3:47 19 Nov '06  
AnswerPInvokeStackImbalance Pinmemberhahnl5:50 14 Apr '06  
AnswerRe: PInvokeStackImbalance PinmemberDieter Buecherl1:36 19 Jun '07  
GeneralRe: Emit? Pinmemberbibiboule4:44 29 Jan '07  
GeneralRe: Emit? Pinmemberbibiboule4:58 29 Jan '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 15 Jun 2004
Editor: Sean Ewington
Copyright 2004 by Harkos
Everything else Copyright © CodeProject, 1999-2009
Web12 | Advertise on the Code Project