Search code examples
c#c++pinvokemarshallingdllimport

PinvokeStackImbalance occured. This exception is thrown but i have no idea what the error is?


The exception:

Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'D:\UI_still_in_progress\user_interface\bin\Debug\WindowsFormsApplication1.vshost.exe'.

Basically my application has a background real time process in c++ which communicates with a micro-processor. Ive set up a c++ dll file which allows me to pass data from the c++ application and c# UI in real time.

My problem is that I have to create a finite state machine to make sure the physical device and c++ app are all doing what they should be based on whats going on in the UI. so my goal is to pass simple integers representing the states from c# to c++.

The real time data is passing from c++ to c# with no issues using the following implementation:

c++ .h file snippet:

extern "C" __declspec (dllexport) int grabx();
extern "C" __declspec (dllexport) void setFSMstate(int s);

C++ .cpp file snippet:

int grabx()
{
    return x0;
}

void setFSMstate(int s)
{
    state = s;
}

c# class

public class MyDllMethods {

    [DllImport("dllTESTER.dll")]
    public static extern int grabx();

    [DllImport("dllTESTER.dll")]
    public static extern void setFSMstate(int s);
}

c# method calls

xx = MyDllMethods.grabx();
MyDllMethods.setFSMstate(2);

The grabx() function works fine and i can read the x value from the device in real time. When my UI moves to a new screen i attempt to change the state in every application by passing a 2 into setFSMstate(). When this method is called the exception is thrown.

I am quite new to P/Invoke so if there is anything I missed please help out. I may have marshaled it wrong but im confident that type int is the same in c# and c++?


Solution

  • The problem you're running into is a calling convention mismatch. Calling conventions specify how parameters are passed to functions, and how their return values are passed back to the caller. Your C++ functions are being declared with an implicit calling convention of __cdecl, but P/Invoke's default calling convention is __stdcall.

    To fix this, you just have to change your P/Invoke configuration:

    [DllImport("dllTESTER.dll", CallingConvention = CallingConvention.Cdecl)]
    

    (The reason you didn't have any problems with your first method is that it doesn't have any parameters—__cdecl and __stdcall pass parameters differently, but they both specify that integer return values are stored in the EAX register.)