P/Invoke was mainly designed to interoperate with the Windows API, so in uses the StdCall
convention by default. C uses the Cdecl
convention by default. You need to change on of the sides to explicitly specify the calling convention so it matches on both sides.
Your classical DLL import specifies the convention with [DllImport(..., CallingConvention=CallingConvention.Cdecl)
, the variant based on GetDelegateForFunctionPointer
does not specify a calling convention (and thus uses StdCall
). You need to specify it with [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
.
Your code is just as wrong without a debugger attached, it just hides the error. Normally such a mismatch would unbalance the stackpointer leading to an instant crash, but the .net marshalling code seems to have special handling for the stackpointer avoiding that crash. Without a debugger is silently swallows the error, with a debugger it displays it.