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

Contents

Introduction

The idea for this article emerged from a failed attempt at solving a problem using an embedded Firebird database with ASP.NET. There is a great article on CodeProject on Embedded Firebird by Dan Letecky, but it is not a prerequisite for this article.

Background

The Firebird ADO.NET Provider communicates with Firebird native DLL's. While using the embedded version, it communicates through API with 'fbembed.dll' which should be located in the bin directory of your application. ASP.NET web application assemblies, when run, get transferred to the "temporary ASP.NET files" directory. For instance:

'c:\windows\microsoft.net\framework\v1.1.4322\temporary asp.net files\webapplication1\946ca3d2\5cefcc750'

The 'fbembed.dll' DLL isn't a .NET assembly; it's a native DLL and therefore isn't copied to that directory. So after doing exhaustive Googling I came upon a code sample by Mattias Sj�gren (Dynamic PInvoke method calls), which describes a method for building dynamic assemblies that contain API methods and changing their DLL paths. This article is the basis for my article.

The idea was to change the source of Firebird Provider so that all the hard coded references of 'fbembed.dll' are changed during runtime to point to the correct location of the DLL. I tried to get the correct path of the web application DLL by using Assembly.GetCallingAssembly().Location, thinking that the calling assembly inside the provider would be the web application assembly. That call would return the path to the web application assembly where I could copy 'fbembed.dll' during runtime. This turned out to be very strange because that location pointed to the Firebird Provider assembly. For some reason the provider called itself! So I gave up that approach.

The purpose of this article

I am trying to give you a possible solution to the problem of using native DLL's whose path is not hard coded in the application. Sometimes it is not possible to put your own DLL's into the system32 directory, and sometimes it is not possible to use them even if they are in your bin directory (e.g. ASP.NET).

Concept and code

Instead of using the Firebird Provider source code, I made a simple class that holds a few API's (MessageBox and MessageBoxEx).

This approach consists of three parts.

  1. The original API class (hard coded DLL paths).
  2. The converter method.
  3. The surrogate class that other parts of your project call instead of the original API functions.

The converter method uses the original class as a template and makes a dynamic assembly that has API functions pointing to any path of your choice. Then the surrogate communicates with the dynamic class.

You may be asking yourself, will I have to ever change the calling method in the project to be able to call methods in the surrogate class. The answer is no, you won't. The surrogate will be using delegates with the same name, footprint, etc. and will have the same name as your original class. Just wait and see...

This is just a temporary solution before .NET 2.0 is released. It has some pretty cool methods that can make a delegate from a function pointer obtained by the LoadLibrary API.

The original API class - The template

/// <summary>

/// Used as a API class "template"

/// </summary>

public class SimpleApiOriginal
{
    // All api sould be declared static 

    // public because that is how

    // my code detects api methods from the rest

    [DllImport("user32.dll")]
    public static extern int MessageBoxEx(IntPtr hWnd, 
                        string lpText, string lpCaption,
                        uint uType, ushort wLanguageId);

    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    public static extern int MessageBox(IntPtr hWnd, 
                        String text, String caption, 
                        uint type);
}

This class serves as a template for the converter. The API's should be declared as public static. The reason for that will be explained later. Notice that all the API's refer to "user32.dll", this is important because the converter method assigns the same DLL path to all the functions. This could be improved in the converter, or you could create separate template classes for different API DLL's.

The dynamic API class - The generated class

The dynamic class has the same API functions, however the DLL path is changed.

public class SimpleApiDynamic
{

    [DllImport("c:\\Windows\\system32\\user32.dll")]
    public static extern int MessageBoxEx(IntPtr hWnd, 
                        string lpText, string lpCaption,
                        uint uType, ushort wLanguageId);

    [DllImport("c:\\Windows\\system32\\user32.dll", 
                                  CharSet=CharSet.Auto)]
    public static extern int MessageBox(IntPtr hWnd, 
                 String text, String caption, uint type);
}

I changed the path to point to the user32.dll in 'c:\Windows\system32' directory. This was, of course, totally unnecessary because the original functions already pointed to the same DLL, but it just goes to show that you could change it to whatever you like.

The surrogate class

Instead of calling the API's in the original class, you would call delegates (yes delegates; I love .NET!) in the surrogate class the same way you would call the original API functions. No difference. Those delegates on the other hand call the API's in the dynamic class. This surrogate class would have a static constructor (a type constructor) that builds the dynamic class and binds the delegates to the methods in the dynamic class before calling them.

public class SimpleApi
{

    // notice the same function parameters and return 

    // types here and in the original API class 

    public delegate int MessageBoxExDelegate(IntPtr hWnd, 
                         string lpText, string lpCaption,
                         uint uType, ushort wLanguageId);
    public delegate int MessageBoxDelegate(IntPtr hWnd, 
                  String text, String caption, uint type);

    // the local delegate variables are named 

    // exactly like the original API functions

    // calling them is easy: 

    //        SimpleApi.MessageBox(something, 

    //                           "Testing", "Caption", 0);

    //

    public static MessageBoxExDelegate MessageBoxEx;
    public static MessageBoxDelegate MessageBox;
    
    // holds the dynamicly created type

    private static Type _dynamicType;
    
    /// <summary>

    /// Initializes the SimpleApi type by 

    /// creating SimpleApiDynamicType and

    /// binding the delegates to it

    /// </summary>

    static SimpleApi()
    {
        // CreateDynamicType is the method 

        // that builds our dynamic type

        _dynamicType = 
             CreateDynamicType(typeof(SimpleApiOriginal), 
                                        "SimpleApiDynamic")

        if (_dynamicType == null)
            throw new Exception();


        // bind a delegate on a type method

        // this works if the original (and 

        // of course dynamic) api is static

        MessageBox = Delegate.CreateDelegate(
                      typeof(MessageBoxDelegate),
                     _dynamicType.GetMethod("MessageBox")) as 
                                           MessageBoxDelegate;
                                
        MessageBoxEx = Delegate.CreateDelegate(
                typeof(MessageBoxExDelegate),
                _dynamicType.GetMethod("MessageBoxEx")) as 
                                         MessageBoxExDelegate;

    }
        
    /// <summary>

    /// Creates the dynamic type.

    /// </summary>

    /// <param name="originalType">Type of 

    /// the original API class</param>

    /// <param name="dynamicBaseName">Base name 

    /// of the dynamic base. (eg. SimpleApiDynamic"</param>

    /// <returns></returns>

    private static Type CreateDynamicType(Type originalType, 
                                          string dynamicBaseName)
    {
        ...
    }
}

Calling the delegated methods of this class is quite simple:

SimpleApi.MessageBox(new IntPtr(0), 
   "New message text", "MessageBox caption!", 0);

Notice that it is absolutely the same as calling the API's in the original. The only thing you have to change in your project is to make a new class that holds all the delegates (the surrogate class) with the same name as your original class and rename your original class to something else or move it to another namespace.

The magic

The converter method is using reflection to retrieve method descriptions in the original class and their parameter descriptions. Based on that data it uses a slightly modified version of Mattias' algorithm to build the dynamic type.

We will go over the code in the CreateDynamicType method step by step:

// Create dynamic assembly 

AssemblyName assemblyName = new AssemblyName();

// nothing fancy, "...Asembly", could be anything

assemblyName.Name = dynamicBaseName + "Assembly"; 

// The AssemblyBuilderAccess.RunAndSave attribute 

// allows me to save this assembly later 

// on so I can dissasemle and check it. In your release 

// version it will be sufficient to use 

// AssemblyBuilderAccess.Run because there 

// is no need to save it.

AssemblyBuilder assemblyBuilder = 
   AppDomain.CurrentDomain.DefineDynamicAssembly(
                 assemblyName,
                 AssemblyBuilderAccess.RunAndSave);

// Add module to assembly

// I'm using an overloaded constructor 

// here that creates a persistent module 

// which can be saved to the disk

ModuleBuilder moduleBuilder = 
    assemblyBuilder.DefineDynamicModule(
                       dynamicBaseName + "Module",
                       dynamicBaseName + ".dll");

// Add a type to the module 

TypeBuilder typeBuilder = 
    moduleBuilder.DefineType(dynamicBaseName + "Type", 
                                 TypeAttributes.Class);

So far, we just created a dynamic assembly with a module that contains an empty type. Next we have to create the API functions with modified paths. We do that by iterating through the public static methods of the original API class (originalType):

// retrieve all the methods that are public 

// and static in the originalType

MethodInfo[] minfos = 
    originalType.GetMethods(
        BindingFlags.Public | BindingFlags.Static);

// loop through those methods

for (int i = 0; i < methodInfos.GetLength(0); i++)
{
    ...
}

Inside the loop we collect the method's parameters and save their types and attributes:

    // mi holds the info for an api method

    MethodInfo mi = methodInfos[i];

    // get all method parameters so we can use 

    // thier types and attributes later on

    ParameterInfo[] methodParameters = mi.GetParameters();
    int parameterCount = methodParameters.GetLength(0);

    // variables to store parameter types and attributes

    Type[] parameterTypes = new Type[parameterCount];
    ParameterAttributes[] parameterAttributes = 
                new ParameterAttributes[parameterCount];

    // save method parameter types and attributes

    for (int j = 0; j < parameterCount; j++)
    {
        parameterTypes[j] = methodParameters[j].ParameterType;
        parameterAttributes[j] = methodParameters[j].Attributes;
    }

Next we create a MethodBuilder that allows us to add a PInvoke method in the dynamic type based on the original. The second parameter of the DefinePInvokeMethod method is the path to the DLL. I'm using the function GetDynamicDllPath that returns a string containing the path to the DLL.

    //create a MethodBuilder for a PInvoke method

    MethodBuilder methodBuilder = 
        typeBuilder.DefinePInvokeMethod(
            mi.Name, // use same name as API function

            GetDynamicDllPath(), // here we change the 

                                 // dynamic path of the dll's

            mi.Attributes, // original method attributes

            mi.CallingConvention, // original method 

                                  // calling convention

            mi.ReturnType, // original method return type

            parameterTypes, // the method parameter 

                            // types we collected

            CallingConvention.StdCall, // StdCall interop calling 

                                       // convention (possible problem)

            CharSet.Auto);
        
    // we have to additionally define 

    // the parameter Attributes and

    // set them the same as the original 

    // parameter attributes

    for (int j = 0; j < parameterCount; j++)
        methodBuilder.DefineParameter(j + 1, 
                     parameterAttributes[j], 
                     methodParameters[j].Name);

    // We set the implementation flags 

    // the same as the original method

    methodBuilder.SetImplementationFlags(
            mi.GetMethodImplementationFlags());

This is the end of the loop and there are just a few more lines before we complete the converter.

    // create the defined type

    Type retval = typeBuilder.CreateType();

    // save the dll in the bin directory, 

    // not necessary but informative.

    // you can use Lutz Roeder's .NET Reflector to open it.

    assemblyBuilder.Save(dynamicBaseName + ".dll");

    // finally return the dynamic type!

    return retval;

Using the method in your projects

Once more I will roughly explain the whole process.

  1. Rename your template API class to something else.
  2. Create a surrogate class with the same name as the original template class.
  3. Create delegates with the same footprint as the original API functions.
  4. Copy the converter method in your surrogate class along with the type constructor.

The hard work lies in writing the delegates and their respective variables. The converter only parses the API functions. API types and constants should be transferred to the surrogate class!

Improvements

Here are some improvements that I could think of:

The converter also makes the dynamic API functions use CallingConvention.StdCall, but not all APIs use that calling convention. That could also be improved...

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralMarshalling: Using native DLLs in .NET
meukjeboer
5:33 29 Aug '08  
Please read this blog post:
http://blog.rednael.com/2008/08/29/MarshallingUsingNativeDLLsInNET.aspx[^]

It shows which types are interoperable, how to import a DLL, how to pass strings and how to de-reference pointers.
Generalgive me a hint
massimo_ negroni
9:21 23 Apr '07  
Thanks for this,
I've wrote a faq and some hint on this and you can check on

www.marichitas.com/expert_view.aspx on section ASP.NET how to use
dllimport with ASP.NET and solve some problem.

Massimo
GeneralError when call function in DLL
thanhhhhhh
20:30 7 Jul '06  
Hi,
Normally, I call direct dll using specific path, my function in dll work good. However, when I do following this article, my function in dll do not receive enough.

...
public static extern bool myFunction (char[] myVar);
...

In SimpleApi:
public delegate bool myFunction(char[] myVar);
...

When I call:
String str = "Hello";
SimpleApi.myFunction (String2char(str));
//String2char, is my function, use for converting String to char array.
In my dll, myVar is only 'H'.


QuestionA problem with P/Invoke
t4urean
8:03 16 Apr '06  
I am involved in a project whereby I have to use a methods from a Win32 dll. There are two methods for which I can't write signature in C#


I have even tried using p/invoke wizard, but it didn't gave the right answer.

The C/C++ declaration for these methods is
BOOL __stdcall CodecStart(int hRadio,void __stdcall (*CallbackFunc)(void *),void *CallbackTarget);

and
DWORD __stdcall CodecRead(int hRadio, void *Buf, DWORD Size); 

I would highly appreciate if any2 can suggest the signature that I can use in C#


o O º(`'·.,(`'·., ☆,.·''),.·'')º O o°
»·'"`»* *☆ t4ure4n ☆* *«·'"`«
°o O º(,.·''(,.·'' ☆`'·.,)`'·.,)º O o°
AnswerRe: A problem with P/Invoke
fkoestner
9:17 15 Jun '06  
You need to declare a delegate to pass as a callback. This article might help:

Using C calling convention callback functions in C# and VB - the easy way[^]


-Fred
Generalreleasing resources
fatih isikhan
4:11 5 Dec '05  
do you have any idea for releasing resources (such as pointers) after terminating the C# program
AnswerRe: releasing resources
Fran Pregernik
21:45 8 Dec '05  

If you are refering to my example above, then I'm not using creating any unmanaged pointers. Everything above is managed. So there is no need to release pointers.

If I'm wrong, please tell me.
GeneralRe: releasing resources
fatih isikhan
1:21 9 Dec '05  
i am asking you in general how we could release the resources (like pointers). I didn't ask you for the program above.

thanks for your consideration
GeneralCalling a DLL is there any method other than "GetProcAddress"
amitcjoshi
20:10 12 Oct '05  
Hi Friends,
I am using VC version 7.1 (.NET) in that case I am calling an APIs from Library whose path I am giving in the main function. Now the problem is I am using "getprocaddress" every time to call API . Is ther5e any other method which can directly call the API ????
If I give the path of DLL in the Project settings... I am getting some memory Leak in only version 7.1 I am not getting any Memory Leak while calling DLL in VC version 6.0.
So please suggest some solution keeping the above considerations in Mind.

Thanking You in Advance,


Amit Joshi

-- modified at 1:13 Thursday 13th October, 2005
GeneralYuck
vbrtsdfvdsf
12:38 15 Aug '05  
Holy cow that's nasty. Isn't there a better way to achieve loose binding?
GeneralRe: Yuck
dannyres
19:26 15 Aug '05  
I suppose you could always use LoadLibrary, GetProcAddress and so on to load, find and call the function.. though that isnt completely 'late bound' as you have to rely on those API's..

Daniel
GeneralRe: Yuck
Fran Pregernik
21:12 15 Aug '05  
Hi,

in .Net 2.0 there will be a function that takes GetProcAddress argument (function pointer) and creates a Delegate (I think in the Marshal class GetDelegateFromPointer or something). Then you could cast that generic delegate into your specific that mimics the api you want to call, like the ones I mentioned in my article!

But that's in .NET 2.0.... can't wait Smile)

In .NET 1.x using LoadLibrary, GetProcAdress is a pain because you would have to write a method for each of your API functions that calls, somehow I don't know how, probably some other API, the function pointer you get from GetProcAddress, passes the function parameters and so on. So if you had like 10 API functions, imagine the code you would have to write for each one of them.

Here the only pain is in writing delegates that look like API's and that can be done with some simple Search-Replace. The converter is generic, just copy paste!

Regards,
Fran Pregernik
GeneralRe: Yuck
Fran Pregernik
0:18 17 Aug '05  
Here is a nice example for .NET 2.0

http://blog.joycode.com/junfeng/archive/2004/07/05/26674.aspx

and this

http://blogs.msdn.com/devinj/archive/2005/07/12/438323.aspx
GeneralRe: Yuck
Flier Lu
4:28 18 Aug '05  
There is some old articles on my blog maybe useful.

like this:

http://www.cnblogs.com/flier/archive/2004/07/22/26345.html

and some about dynamic proxy, I think dynamic proxy is better than static delegate:

http://www.cnblogs.com/flier/archive/2004/10/16/53145.html
http://www.cnblogs.com/flier/archive/2004/10/17/53188.html

http://www.cnblogs.com/flier/archive/2005/02/08/103429.html
http://www.cnblogs.com/flier/archive/2005/02/09/103528.html

no time to translate to english, sorry :P
GeneralRe: Yuck
valex123
12:48 17 Aug '05  
I have been trying to use the embedded Firebird fbembed.dll with asp.net and had the same problem you had, i.e. the firebird ado.net provider could not find fbembed.dll in the bin directory.

While doing some research into the problem, I saw a suggestion to use LoadLibrary to manually load the fbembed.dll (provide full path to dll) before calling any function or procedure in firebirdsql.data.firebird. This worked. Here is some sample code

Declare Function LoadLibrary Lib "kernel32" Alias _
"LoadLibraryA" (ByVal lpLibFileName As String) As Integer

Declare Function FreeLibrary Lib "kernel32" Alias _
"FreeLibrary" (ByVal hLibModule As Integer) As Integer

Dim hLib As Integer = LoadLibrary("c:\maximumasp\www\bin\fbembed.dll")
dim cnstring as string="DataSource=localhost;ServerType=1;User=SYSDBA; _
Password=masterkey;Dialect=3; _
Database=c:\maximumasp\www\data\firebird\mydb.fdb"
dim cn as FbConnection=new FbConnection(cnstring)
cn.Open()
cn.Close
cn=nothing
FreeLibrary(hLib)

Good luck.

Vince
GeneralRe: Yuck
Fran Pregernik
21:16 17 Aug '05  
Hi,

thanks for the idea!

So you are saying that .NET somehow reuses the loaded libraries instead of trying to load them again. Interesting! I'll look it up.

The way I solved it was to use several different methods:

1. fix the PATH env. variable to point to fbembed.dll
2. copy the fbembed.dll to the same directory as Assembly.GetExecutingAssembly.Location of my WebApplication

Once more, thank you for the idea!

Best regards,
Fran Pregernik
GeneralRe: Yuck
Fran Pregernik
20:43 18 Aug '05  
Hi, I've been testing your code.

First test: Call to LoadLibrary before opening connection

IntPtr ptr = LoadLibrary("d:\\bin\\fbembed.dll");

string connStr = "..";
FbConnection fbc = new FbConnection(connStr);
fbc.Open();

FreeLibrary(ptr);

Test worked! Great

Second test: Call to LoadLibrary and FreeLibrary before opening connection

IntPtr ptr = LoadLibrary("d:\\bin\\fbembed.dll");
FreeLibrary(ptr);

string connStr = "..";
FbConnection fbc = new FbConnection(connStr);
fbc.Open();

Test also worked! Great

Third test: Two subsequent calls to LoadLibrary

IntPtr ptr = LoadLibrary("d:\\bin\\fbembed.dll");
IntPtr ptr2 = LoadLibrary("fbembed.dll"); // This is how the FB ADO.NET Provider calls the dll

FreeLibrary(ptr);
FreeLibrary(ptr2);

The two intptr's had the same value (e.g. pointed to the same memory location)!

Fourth test: LoadLibrary-FreeLibrary-LoadLibrary-FreeLibrary

IntPtr ptr = LoadLibrary(Server.MapPath("~\\bin\\fbembed.dll"));
FreeLibrary(ptr);
IntPtr ptr2 = LoadLibrary("fbembed.dll");
FreeLibrary(ptr2);

Again the pointers pointed to the same mem location.

Fifth test: SetDllDirectory instead of LoadLibrary

[DllImport("kernel32.dll")]
static extern bool SetDllDirectory(string lpPathName);

...

SetDllDirectory(Server.MapPath("~\\bin"));

string connStr = "...";
FbConnection fbc = new FbConnection(connStr);
fbc.Open();

Didn't work.


First I thought that LoadLibrary calls SetDllDirectory and sets the path, but the fifth test proved that to be wrong. The second idea was that LoadLibrary loads the dll into memory and any subsequent calls to LoadLibrary references that, but the fourth test should release the first dll (full path) and load a new dll (just dll name) which it does but the pointers were the same...

All in all LoadLibrary really could be used for late binding native dll's.





Best regards,
Fran Pregernik
GeneralRe: Yuck
Domenic
8:28 1 Sep '05  
Did you ever investigate using tlbexp or ildasm to create a type library or an Interop.* version of the native API. I seem to remember I did that once. I believe it was with the Visual SourceSafe dll.
GeneralRe: Yuck
Fran Pregernik
2:01 2 Sep '05  
Domenic wrote:
Did you ever investigate using tlbexp or ildasm to create a type library or an Interop.* version of the native API. I seem to remember I did that once. I believe it was with the Visual SourceSafe dll.

Hi,

that sounds interresting. Could you elaborate a bit more?

GeneralRe: Yuck
Domenic
3:43 2 Sep '05  
This article gives in detail how to create the Interop version of your COM dll:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcongeneratingprimaryinteropassemblies.asp[^] In addition, a trick I needed to use was to make private member functions public. You can do this by running ildasm on the interop dll, editing the IL language, and recompiling the file as the Interop Dll again using ilasm. Good luck.
GeneralRe: Yuck
Fran Pregernik
3:54 2 Sep '05  

Yes, but here we are talking about native dll's (API and such) not COM dll's.

Regards


Last Updated 14 Aug 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010