Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using the _CDECL calling convention in C# (changing compiler service)

0.00/5 (No votes)
22 Dec 2005 1  
An article on using the __cdecl callback from C#, changing compiler service.

Introduction

The C# compiler, by default, follows the __stdcall calling convention. Now, if we have an existing unmanaged DLL which follows the __cdecl calling convention, then how do we fit that into our C# code.

Suppose we have a C++ unmanaged code with the following declaration:

// Function pointer declaration.

typedef  int (__cdecl *funcptr)( int, int);

//Using that function pointer 

int UseFuncptr(int a,int b,funcptr fptr)
{
    return(fptr(a,b));
}

If we want to implement this function from C#, we can implement it this way:

[DllImport(�dllname.dll�)]
public static extern int UseFuncptr(int a,int b,MulticastDelegate callbk);
//This delegate will follow __stdcall

public delegate int mydg(int a,int b);

[STAThread]
static void Main(string[] args)
{
    int a = 10, b = 10;
    mydg dg = new mydg(Sum);

    a = UseFuncptr(a, b, dg)

    ����


}

//Trying to implement this method through callback    

public static int Sum(int a,int b)
{
    return (a+b);
}

Here, mydg will not serve our purpose to call the method Sum passing its address to the unmanaged method UseFuncPtr as we are using a different calling convention from that in the unmanaged DLL. On executing this code, it will throw a run time error:

Background

This problem occurs due to a mismatch in the calling conventions of C# and C or C++. The default calling convention in C# is __stdcall whereas in C or C++ it is __cdecl.

The Microsoft .NET IDE does not provide a way to change the calling convention, so there is no easy way to call such a method.

Using the code

Now, to implement the __cdecl calling convention in C#, we need to inject a part to the MSIL code.

Our main aim is to change the calling convention of our delegate (mydg) in the JIT compiler. After building our application, we will create the .IL file from the .exe or .dll using the ILDASM tool provided by .NET.

There we find the delegate declaration as:

.method /*0600000C*/ public hidebysig virtual instance int32  Invoke(int32 a,
              int32 b) runtime managed
      // SIG: 20 02 08 08 08

      {
      } // .......:Invoke

Now, we have to change the compiler service to __cdecl. We will add the following line to the IL code:

.method /*0600000C*/ public hidebysig virtual instance
 int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)  
Invoke(int32 a,
              int32 b) runtime managed
      // SIG: 20 02 08 08 08

      {
      } // ................::Invoke

Now, we will save this to the IL file. And then build this IL to the corresponding .exe or .dll using ILASM.

>ILASM /exe ILfilename.IL

This will create our deserved executable which is compatible with the __cdecl calling convention. This way, we can easily implement the __cdecl calling convention in C#.

Points of Interest

Suppose in your code you have, say 20 delegates, now the question arises about how to identify the exact point in the MSIL code where we need to inject our code. I found a way around for this problem using the Attribute class. We will create a simple attribute class:

[AttributeUsage(AttributeTargets.Delegate)]
public class CallconvAttribute : System.Attribute
{
    public CallconvAttribute()
    {

    }
}

and we will declare our delegate mydg as:

[Callconv]public delegate int mydg(int a,int b);

Now, if we build this .exe or .dll and Dump the IL through ILDASM, we can easily identify the delegate through the attribute class. The MSIL will look like this.

.custom /*0C00000D:0600000F*/ instance void 
     CallDLLFromDotNet.CallconvAttribute/* 02000004 */::.ctor() 
     /* 0600000F */ = ( 01 00 00 00 ) 
  .method /*0600000B*/ public hidebysig specialname rtspecialname 
          instance void  .ctor(object 'object',
                               native int 'method') runtime managed
  // SIG: 20 02 01 1C 18

  {
  } // ...............::.ctor


  .method /*0600000C*/ public hidebysig virtual 
          instance int32  
          //Here I need to inject that code.

          Invoke(int32 a,
                                 int32 b) runtime managed
  // SIG: 20 02 08 08 08

  {
  } // .................::Invoke

Now, the code injection will be much easier. With the CallconvAttribute, we can easily identify our delegate declaration.

The Code

In the source code, you will find a .NET console application with an unmanaged DLL where we implement the whole concept. Happy MSIL learning..

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here