PInvokeStackImbalance not called by CallingConvention

I do not know what the problem is.

I have a ton of p / invoke calls that work without incident ... other than that.

I managed to reduce my problem to the following code sample.

If I remove any struct element (either double or int), it works fine.

I suppose the problem is with the layout of the structure, but when I do sizeof () in C and Marshal.SizeOf () in C #, they both return the same value ... so if the size of the structure is the same in C # and C, that maybe a problem?

I am clearly missing something basic.

SampleDLLCode.c

#pragma pack(1) typedef struct SampleStruct { double structValueOne; int structValueTwo; } SampleStruct; __declspec(dllexport) SampleStruct __cdecl SampleMethod(void); SampleStruct SampleMethod(void) { return (SampleStruct) { 1, 2 }; } 

Build script

 gcc -std=c99 -pedantic -O0 -c -o SampleDLLCode.o SampleDLLCode.c gcc -shared --out-implib -o SampleDLL.dll SampleDLLCode.o 

C # code

 using System; using System.Runtime.InteropServices; namespace SampleApplication { [StructLayout(LayoutKind.Sequential, Pack=1)] public struct SampleStruct { public double structValueOne; public int structValueTwo; } class Program { [DllImport("SampleDLL.dll", CallingConvention = CallingConvention.Cdecl)] public static extern SampleStruct SampleMethod(); static void Main(string[] args) { SampleStruct sample = SampleMethod(); } } } 
+7
source share
1 answer

First of all, let me congratulate you on a very well-asked question. It was an admiration, this time, to get all the code needed to reproduce the problem.

The problem is related to the slightly different ABIs used by gcc and Microsoft tools for function return values. For return values ​​that can fit into registers, for example, int return values, there is no difference. But since your structure is too large to fit in one register, and in this situation there are differences between the APIs.

For large return values, the caller passes a hidden function pointer. This hidden pointer is pushed onto the stack by the caller. The function writes the return value to the memory address indicated by this hidden pointer. The difference in ABI is who removes the hidden pointer from the stack. Microsoft tools use ABI, which requires the caller to invoke a hidden pointer, but by default gcc ABI asks the caller to do so.

Now that gcc is almost infinitely configurable, there is a switch that allows you to control the ABI. And you can use gcc the same rules as Microsoft tools. This requires the callee_pop_aggregate_return attribute.

Change your code to C as follows:

 __declspec(dllexport) SampleStruct __cdecl SampleMethod(void) __attribute__((callee_pop_aggregate_return(0))); // specifies that caller is responsible for popping the hidden pointer SampleStruct SampleMethod(void) { return (SampleStruct) { 1, 2 }; } 
+9
source

All Articles