Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / MSIL
Article

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

Rate me:
Please Sign up or sign in to vote.
4.30/5 (16 votes)
22 Dec 20052 min read 86.2K   479   24   11
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:

C#
[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:

Image 1

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:

MSIL
.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:

MSIL
.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:

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

    }
}

and we will declare our delegate mydg as:

C#
[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.

MSIL
.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


Written By
India India
Mainak Saha is an Aerospace Engg from Indian Institute Of Technology Kharagpur(2003 batch) .... Now working with Cognizant Technology Solutions ... Think himself as one of the many C# gurus .. Smile | :)

Comments and Discussions

 
GeneralSolved in .Net 2.0 Pin
piotr_fed1-Oct-09 12:01
piotr_fed1-Oct-09 12:01 
GeneralHello Pin
Nadav_h4-Mar-06 19:51
Nadav_h4-Mar-06 19:51 
first your article is realy nice and interesting.
second
i have some question about C# compilers

i trying to add extension to the C# compiler
and i didnt find any way how to
do it can u give me an hint how to do this?
i trying to do somting like in this article -->
http://mark.michaelis.net/Blog/CompileTimeAttributesInNET.aspx

10x
Nadav
GeneralRe: Hello Pin
Mainak Saha11-Mar-06 17:01
Mainak Saha11-Mar-06 17:01 
QuestionWhy DLL? Pin
AlexEvans14-Jan-06 15:13
AlexEvans14-Jan-06 15:13 
AnswerRe: Why DLL? Pin
Mainak Saha14-Jan-06 18:36
Mainak Saha14-Jan-06 18:36 
NewsOr Use CallingConvention.Cdecl... Pin
scott willeke27-Dec-05 6:06
scott willeke27-Dec-05 6:06 
QuestionRe: Or Use CallingConvention.Cdecl... Pin
Naldo Alcalde28-Dec-05 9:43
Naldo Alcalde28-Dec-05 9:43 
GeneralRe: Or Use CallingConvention.Cdecl... Pin
Mainak Saha28-Dec-05 17:23
Mainak Saha28-Dec-05 17:23 
GeneralRe: Or Use CallingConvention.Cdecl... Pin
scott willeke28-Dec-05 19:35
scott willeke28-Dec-05 19:35 
GeneralRe: Or Use CallingConvention.Cdecl... Pin
Naldo Alcalde29-Dec-05 5:04
Naldo Alcalde29-Dec-05 5:04 
Generalinteresting Pin
Super Lloyd22-Dec-05 14:31
Super Lloyd22-Dec-05 14:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.