C ++ DLL, вызываемая из C ++ Console App Works, вызываемая из C # Console App, имеет переполнение стека - PullRequest
2 голосов
/ 20 апреля 2011

Я получил код C / C ++ для использования в другом проекте.Я положил его в DLL, а затем вызвал DLL из C ++.Он работал нормально и соответствовал результатам, когда код был просто вызовом функции.

Однако затем я попытался заставить DLL работать из приложения C #.Я преобразовал тестовый жгут и сделал вызов DLL, но получаю исключение переполнения стека.

В C ++ я добавил:

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

И вызвал такую ​​функцию:

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

Где заголовок содержит:

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);

В C # я использую:

using System.Runtime.InteropServices;

и

[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);

С вызовом функциинапример:

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

Функция DLL вызывается много раз в цикле for.Код C ++ работает просто отлично.Код C # выдает ошибку переполнения стека.Я добавил несколько отладочных состояний в функцию proxy_rec_main, и кажется, что она попадает в каждое утверждение до ее возврата.Но, похоже, выдает ошибку при возврате из функции.Любые идеи будут приветствоваться.

Спасибо.

Ответы [ 3 ]

2 голосов
/ 20 апреля 2011

Свойство CallingConvention отсутствует в объявлении [DllImport].Нет никаких признаков того, что вы используете __stdcall в объявлении C, поэтому скорее всего потребуется CallingConvention.Cdecl.Это действительно может вызвать SO, стек не очищается.

Debug + Windows + Registers и наблюдают значение регистра ESP до и после вызова, оно должно быть одинаковымЕсли вы отключили предупреждение управляемого отладчика PInvokeStackImbalance, обязательно включите его снова.Игнорирование этого предупреждения не очень хорошая идея.

И отладьте собственный код, проверьте значения переданного аргумента.Есть так много аргументов, что одной неверной декларации для одного из них достаточно, чтобы выстрелить ногой.

1 голос
/ 20 апреля 2011

Ref двойной массив кажется проблематичным, Добавьте к нему [MarshalAs] и передайте IntPtr, а не двойной массив.

.net массивы не являются указателями, как в C ++.

Отметить этот метод c # как приватный, обернуть открытым методом, который использует Marshal.Copy для передачи возвращенного указателя в массив .net.

Пример передачи массива при размещении в .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);
}

Пример передачи массива при выделении в нативном режиме:

[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);
}

Если сторона c # выделяет массив, не забудьте выделить и освободить в c #. (используя маршала (de), выделите h глобальных методов.)

В C ++ выделяет вызов метода C ++ для освобождения.

0 голосов
/ 20 апреля 2011

Предполагая, что код клея правильный, возможно, просто функция DLL занимает много стека.Я сам столкнулся с подобной ситуацией, и оказалось, что в стеке C ++ размещен какой-то очень большой объект.При вызове из нативного кода перед вызовом использовался только небольшой стек, поэтому в стеке оставалось достаточно места.При вызове из управляемого кода большая часть стека уже была занята, так что не осталось достаточно места.Если вы посмотрите на функцию C ++, которая вызывает переполнение, вы можете увидеть, что она пытается поместить большой объект в стек.

...