Thursday, May 20, 2010

PInvokeStackImbalance in .NET 4.0…I beg your pardon?

It’s been quite some time since I’ve played with PInvoke…mmm - could it be as far back as my .NET 1.1 days…back when I just started to appreciate this little thing called “Garbage Collector”…the days where I just started to realise there’s more to life than sacrificing cute and cuddly animals on that big bloody table called C++…selling my soul to Rational (Purify) in the process…yet still somehow convinced that my just completed sacrifice was in vain and instead I just prepared a feast for the Memory Leak devil…
…and just when you get comfortable in the Heaven of managed code…you get smacked right in the face with some old piece of C code…dancing on your now soft delicate body…a body that’s been marinated from the many years of coding in your CLR play pen… and invoking your privates with a stack of imbalance…SAY WHAT???

PInvokeStackImbalance

I came across this today after upgrading a class library that uses PInvoke to .NET 4.0. Specifically, after running the code, I got a PInvokeStackImbalance detection message from the VS2010 Managed Debugging Assistant (MDA). Obviously, my first thought was, as indicated by the message, that the PInvoke signature didn’t match the types of the C function I’m trying to call. However, I still couldn’t see a problem – all the parameter types seemed correct. Also, since I’m running Win 7 64-bit, I figured possibly this could be a 64-bit issue – so I changed the Platform target in my build settings from Any CPU to x86. Still no success.
In the end, the problem seemed to disappear once I’ve changed my build target for the PInvoke class library to target .NET 3.5 (2.0 also works fine) instead. The code worked fine…however this solution just didn’t smell right to me, so I did a bit more digging…and good thing I did!
Turns out that this issue is mentioned in the .NET 4.0 Migration Issues document under the Interoperability section – I’ve copied the relevant section below for quick reference:
Platform invoke To improve performance in interoperability with unmanaged code, incorrect calling conventions in a platform invoke now cause the application to fail. In previous versions, the marshaling layer resolved these errors up the stack. Debugging your applications in Microsoft Visual Studio 2010 will alert you to these errors so you can correct them.
If you have binaries that cannot be updated, you can include the <NetFx40_PInvokeStackResilience> element in your application's configuration file to enable calling errors to be resolved up the stack as in earlier versions. However, this may affect the performance of your application.
Thus, in earlier versions of .NET, we were able to get away with the incorrect calling convention as the .NET framework silently corrected the issue (alas at the cost of introducing a performance penalty).
Armed with this knowledge, it became apparent that the real source of my PInvoke problem was that the PInvoke calling convention for my method defaulted to StdCall – and I needed to use Cdecl instead. Once I updated the code, I was able to restore my class library to target the .NET 4.0 framework.
  1. [DllImport("libfftw3f-3.dll", EntryPoint = "fftwf_plan_r2r_1d", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
  2. public static extern IntPtr r2r_1d(int n, IntPtr input, IntPtr output, fftw_kind kind, fftw_flags flags);
Over the years I might have turned soft from my lack of frequent C++ coding, but at least today I’ll sleep well knowing there’ll be no platform invoking my stack of imbalance…

5 comments:

codeobjects said...

Hey that's a good blog thanks for sharing the advice.
Billing Software Solution

Flominator said...

The exact entry in app.config:
<runtime>
<NetFx40_PInvokeStackResilience enabled="1"/>
</runtime>

Regards and thanks for posting it,

Flo

notBald said...

Thanks. You saved me some head scratching.

Sandy said...

Thanks for that - that confused me for the better part of a day.

ankit d gr8 said...

In earlier versions of the .NET Framework, the marshaling layer detected incorrect platform invoke declarations on 32-bit platforms and automatically fixed the stack.