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






4.30/5 (16 votes)
Dec 22, 2005
2 min read

88191

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