P/Invoking incorrectly, but the CLR fixes it silently?

A

Alfonso Almazor

Greetings,

I have just noticed something interesting about the CLR's generation of
stub code for p/invoke calls (this is on .NET 2.0):

I am using a library called Cairo for some vector graphics drawing.

On windows, it's a DLL written in plain C, exporting its functions as
CDECL.

To interface with my C# app, I found a managed wrapped called "cairo-
sharp", which seems to work quite well.


The interesting thing is, the wrapper code (also C#) uses DllImport in
its default manner, that is; implicitly setting CallingConvention to
CallingConvention.Stdcall instead of CallingConvention.Cdecl, which
should be used for a CDECL DLL.

Now what _should_ happen is that the stack wouldn't get cleaned up after
a call into the DLL, since my code is assuming STDCALL (callee cleans
stack), and the DLL is using CDECL (caller cleans stack).


However, looking at the stack pointer before and after calls through the
wrapper everything is fine!
Tracing lower into the guts of the CLR using windbg, I seem to be seeing
that the generated stub code is fixing up the stack after the call to the
native DLL even though I didn't ask it to!


So it seems like the CLR auto-detects the calling convention of native
libraries (at least in some cases) and fixes the programmers mistakes.

This is definitely not specified anywere in the documentation..
What gives?


- A.A.
 
B

Ben Voigt [C++ MVP]

Alfonso Almazor said:
Greetings,

I have just noticed something interesting about the CLR's generation of
stub code for p/invoke calls (this is on .NET 2.0):

I am using a library called Cairo for some vector graphics drawing.

On windows, it's a DLL written in plain C, exporting its functions as
CDECL.

To interface with my C# app, I found a managed wrapped called "cairo-
sharp", which seems to work quite well.


The interesting thing is, the wrapper code (also C#) uses DllImport in
its default manner, that is; implicitly setting CallingConvention to
CallingConvention.Stdcall instead of CallingConvention.Cdecl, which
should be used for a CDECL DLL.

Now what _should_ happen is that the stack wouldn't get cleaned up after
a call into the DLL, since my code is assuming STDCALL (callee cleans
stack), and the DLL is using CDECL (caller cleans stack).


However, looking at the stack pointer before and after calls through the
wrapper everything is fine!
Tracing lower into the guts of the CLR using windbg, I seem to be seeing
that the generated stub code is fixing up the stack after the call to the
native DLL even though I didn't ask it to!


So it seems like the CLR auto-detects the calling convention of native
libraries (at least in some cases) and fixes the programmers mistakes.

This is definitely not specified anywere in the documentation..
What gives?

The only thing I can think of is that the name mangling (C-style, not C++)
is sufficiently different (cdecl uses leading underscore, stdcall is
trailing "@N") for p/invoke to detect what's up. Or perhaps it is simply
restoring the stack for all calls, just to be safe.
 
A

Alfonso Almazor

The only thing I can think of is that the name mangling (C-style, not
C++) is sufficiently different (cdecl uses leading underscore, stdcall
is trailing "@N") for p/invoke to detect what's up. Or perhaps it is
simply restoring the stack for all calls, just to be safe.


Quite.

I suppose I was hoping that this behavior is actually documented
somewhere.
I don't like the CLR doing automagic things like this without being asked
to :)

- A.A.
 
R

Robert Simpson

Alfonso Almazor said:
Greetings,

I have just noticed something interesting about the CLR's generation of
stub code for p/invoke calls (this is on .NET 2.0):

I am using a library called Cairo for some vector graphics drawing.

On windows, it's a DLL written in plain C, exporting its functions as
CDECL.

To interface with my C# app, I found a managed wrapped called "cairo-
sharp", which seems to work quite well.


The interesting thing is, the wrapper code (also C#) uses DllImport in
its default manner, that is; implicitly setting CallingConvention to
CallingConvention.Stdcall instead of CallingConvention.Cdecl, which
should be used for a CDECL DLL.

Now what _should_ happen is that the stack wouldn't get cleaned up after
a call into the DLL, since my code is assuming STDCALL (callee cleans
stack), and the DLL is using CDECL (caller cleans stack).


However, looking at the stack pointer before and after calls through the
wrapper everything is fine!
Tracing lower into the guts of the CLR using windbg, I seem to be seeing
that the generated stub code is fixing up the stack after the call to the
native DLL even though I didn't ask it to!


So it seems like the CLR auto-detects the calling convention of native
libraries (at least in some cases) and fixes the programmers mistakes.

This is definitely not specified anywere in the documentation..
What gives?

It's always done this. I think its just part of the overhead of P/Invoke
that it records and fixes up the stack pointer before and after a call.

Robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top