Tim Felty
Tim Felty

Reputation: 152

C++ DLL called from C++ Console App Works, called from C# Console App has stack overflow

I recieved some C/C++ code for use in another project. I put it into a DLL and then called the DLL from a C++ test harness. It worked fine and matched the results from when the code was just a function call.

However, I then tried to get the DLL to work from a C# application. I converted the test harness, and made the DLL call but I am recieving a stack overflow exception.

In C++ I added:

 #include "proxy_rec_02.h"  
 #pragma comment(lib,"proxy_rec_02.lib")  

And called the function like this:

 proxy_rec_main(simtime,mx$gl,mz$gl,ry,start_dig,blytarg_on,blytarg,spread_on,last_call,outputs);

Where the header contains:

void DLL_EXPORT proxy_rec_main(double simtime, double mx$gl, double mz$gl, double ry, int start_dig,  
                               int blytarg_on, double blytarg, int spread_on, int last_call, double *outputs);

In C# I'm using:

using System.Runtime.InteropServices;

and

[DllImport("proxy_rec_02.dll")]
unsafe static extern void proxy_rec_main(double simtime, double mxSgl, double mzSgl, double ry, int start_dig,        
                                  int blytarg_on, double blytarg, int spread_on, int last_call, ref double[] outputs);

With a function call like this:

proxy_rec_main(simtime,mxSgl,mzSgl,ry,start_dig,blytarg_on,blytarg,spread_on,last_call,ref outputs);

The DLL function is called many times in a for loop. The C++ code runs just fine. The C# code throws a stack overflow error. I added some debug statemaints to the proxy_rec_main function and it seems to hit each statement before the function returns. But it seems to throw the error on return from the function. Any insights would be welcome.

Thanks.

Upvotes: 2

Views: 563

Answers (3)

Danny Varod
Danny Varod

Reputation: 18068

The ref double array seems problematic, Add [MarshalAs] to it and pass IntPtr, not a double array.

.net arrays are not pointers, as they are in C++.

Mark that c# method as private, wrap with public method that uses Marshal.Copy to transfer returned pointer to .net array.

Example for transferring array when allocated in .net:

[DllImport(EntryPoint="ExternalMethod"]
private static void ExternalMethodInvoke(
    [MarshalAs(UnmanagedType.SysInt), In] IntPtr);

public void ManagedWrapper(ref double[] array)
{
    IntPtr unmanagedMem = Marshal.AllocHGlobal(1000);
    Marshal.Copy(array, unmanagedMem, 0, 1000);
    ExternalMethodInvoke(unmanagedMem); // use try finally for freeing
    Marshal.Copy(unmanagedMem, array, 1, 1000);
    Marshal.FreeHGlobal(unmanagedMem);
}

Example for transferring array when allocated in native:

[DllImport(EntryPoint="ExternalMethod"]
private static void ExternalMethodInvoke(
    [MarshalAs(UnmanagedType.SysInt), Out] out IntPtr);

[DllImport(EntryPoint="ExternalDeleteArray"]
private static void ExternalDeleteArrayInvoke(
    [MarshalAs(UnmanagedType.SysInt), Out] out IntPtr);

public void ManagedWrapper(ref double[] array)
{
    IntPtr unmanagedMem;
    ExternalMethodInvoke(out unmanagedMem); // use try finally for freeing
    Marshal.Copy(unmanagedMem, array, 1, 1000);
    ExternalDeleteArrayInvoke(unmanagedMem);
}

If c# side allocates array, do not forget to allocate and deallocate in c#. (using marshal's (de)allocate h global methods.)

In C++ allocates, call C++ method to deallocate.

Upvotes: 1

bmm6o
bmm6o

Reputation: 6505

Assuming the glue code is correct, it might just be that the DLL function takes up a lot of stack. I ran into a similar situation myself, and it turned out that there was some very large object allocated on the C++ stack. When called from native code, there was only a little bit stack used before the call so there was enough remaining space on the stack. When called from managed code, a lot of the stack was already consumed so that there wasn't enough space left. If you look at the C++ function that is causing the overflow, you may see that it is trying to put a large object on the stack.

Upvotes: 0

Hans Passant
Hans Passant

Reputation: 941465

The CallingConvention property is missing from the [DllImport] declaration. There's no sign of you using __stdcall in the C declaration so CallingConvention.Cdecl is likely to be required. This can indeed cause an SO, the stack doesn't get cleaned-up.

Debug + Windows + Registers and observe the value of the ESP register before and after the call, it should be same. If you disabled the PInvokeStackImbalance managed debugger warning then be sure to turn it back on. Ignoring this warning is not a good idea.

And debug the native code, verify the passed argument values. There are so many arguments that a single bad declaration for one of them is enough to shoot the foot.

Upvotes: 2

Related Questions