Click here to Skip to main content
Click here to Skip to main content

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

, 22 Dec 2005
Rate this:
Please Sign up or sign in to vote.
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

Share

About the Author

Mainak Saha

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 Pinmemberpiotr_fe1-Oct-09 13:01 
See
 
http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=473
GeneralHello Pinmembernad4-Mar-06 20:51 
GeneralRe: Hello PinmemberMainak Saha11-Mar-06 18:01 
QuestionWhy DLL? PinmemberAlexEvans14-Jan-06 16:13 
AnswerRe: Why DLL? PinmemberMainak Saha14-Jan-06 19:36 
NewsOr Use CallingConvention.Cdecl... PinmemberactiveScott27-Dec-05 7:06 
QuestionRe: Or Use CallingConvention.Cdecl... PinmemberNaldo Alcalde28-Dec-05 10:43 
GeneralRe: Or Use CallingConvention.Cdecl... PinmemberMainak Saha28-Dec-05 18:23 
GeneralRe: Or Use CallingConvention.Cdecl... PinmemberactiveScott28-Dec-05 20:35 
GeneralRe: Or Use CallingConvention.Cdecl... PinmemberNaldo Alcalde29-Dec-05 6:04 
Generalinteresting PinmemberSuper Lloyd22-Dec-05 15:31 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 22 Dec 2005
Article Copyright 2005 by Mainak Saha
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid