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:
typedef int (__cdecl *funcptr)( int, int);
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);
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)
����
}
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 public hidebysig virtual instance int32 Invoke(int32 a,
int32 b) runtime managed
{
}
Now, we have to change the compiler service to __cdecl
. We will add the following line to the IL code:
.method public hidebysig virtual instance
int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)
Invoke(int32 a,
int32 b) runtime managed
{
}
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 instance void
CallDLLFromDotNet.CallconvAttribute::.ctor()
= ( 01 00 00 00 )
.method public hidebysig specialname rtspecialname
instance void .ctor(object 'object',
native int 'method') runtime managed
{
}
.method public hidebysig virtual
instance int32
Invoke(int32 a,
int32 b) runtime managed
{
}
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..