출처: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2350703&SiteID=1

//////////////////////////////////////////////////////////////////////
질문자: 누군가 DLL 호출중 ESP 오류가 난다고 했나보다...
//////////////////////////////////////////////////////////////////////


Hello.

I'm trying to wrap a C++ dll using C#. I don't have access to the source used to create the C++ dll so I cannot alter the header files to declare the C++ callback with __stdcall. All I have is the API information.

Trouble arises when I try to wrap a function that has a callback. I get the following error:

"The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention"

This is the prototype of the C++ callback function:
typedef unsigned long (*QS_CALLBACK)(const QS_MSG * pMsg);

QS_MSG is defined in C++ as:
typedef struct s_QS_MSG
 {
unsigned long     Type;           
QS_RESULT         CompletionCode; 
unsigned long     nContextID;    
QS_RequesterType  eRequesterType; 
QS_DeviceAddress  DevAddr;       
QS_SessionHandle  hSessionID;  
const void *      hHandle;
const void *      MoreInfo;  
} QS_MSG;

My C# prototype is:
public delegate UInt32 QS_CALLBACK(ref QS_MSG pMsg);

  
The arguments match (sort of, with the exception of the const). So maybe it's a calling convention problem?

Here's more of the code:

// QS_RESULT, QS_RequesterType are a enum
// QS_DeviceAddress is a struct
[StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct QS_MSG
        {
            public UInt32 Type;            /* Which message is being sent to the UserCallback */
            public QS_RESULT CompletionCode;  /* Result of request */
            public UInt32 nContextID;      /* Context that caused this request */
            public QS_RequesterType eRequesterType;    /* Context that caused this request */
            public QS_DeviceAddress DevAddr;         /* The device address */
            public IntPtr hSessionID;
            public object hHandle;
            public object MoreInfo;        
        }

// QS_ApplicationType is an enum
[DllImport("ViconNet.dll")]
public static extern IntPtr QS_Initialize(QS_ApplicationType eApplType,
                                                         byte[] ipMe,
       QS_CALLBACK pUserCallback,
                                                                   UInt32 nContextID);

private static QS_CALLBACK cb = new QS_CALLBACK(callbackConductor);
private static IntPtr sessionConductor;

// function which is calling the C++ DLL

private void callingFunction( )
{
// ip is defined here
byte [] ip = getIP( );

sessionConductor = QS_Initialize(QS_ApplicationType.QS_APPLTYPE_CONDUCTOR,
                                                  ip, cb, 0)
}


 
//callback function
public static UInt32 callbackConductor(ref QS_MSG pMsg)
{
// do stuff with pMsg
return 1;
}


The error occurs when returning from the callback. Before the return, I inserted a breakpoint to inspect pMsg. It seems to have all the correct information.  If I change the prototype of the callback to have no parameters, there is no error. How can I resolve this problem without changing the C++ code? Thanks for any feedback.

-Neal







QS_SessionHandle QS_Initialize (QS_ApplicationType eApplType,
                                                const unsigned char ipMe[],
                                                QS_CALLBACK pUserCallback,
                                                unsigned long nContextID);



//////////////////////////////////////////////////////////////////////
별다섯개 답글~: 함수 호출 방식을 바꾸는 방법~~
//////////////////////////////////////////////////////////////////////


 

Hi Neal,

Like you may guessed, the problem is that the function pointer that the unmanaged function accepts should respect the C calling convention but the delegate instance you pass to it does not (it respects the standard calling convention (__stdcall).

If you’re using .net 2.0, you can use UnmanagedFunctionPointer Attribute to Control the marshaling behavior of a delegate signature passed as an unmanaged function pointer to or from unmanaged code. Check out the following example:

Code Block

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]

public delegate UInt32 QS_CALLBACK(ref QS_MSG pMsg);


 

If you’re using .net 1.1 then you have to check out the solution here: Using C calling convention callback functions in C# and VB - the easy way.

Hope this helps!

Thanks!








AND