свободная память не очищает блок памяти

Я использую DllImport для вызова метода в библиотеке оболочки c из моего собственного класса .net. Этот метод в c dll создает строковую переменную и возвращает указатель строки.

Что-то вроде этого;

_declspec(dllexport) int ReturnString()
{
 char* retval = (char *) malloc(125);
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return (int)retval;
}

Затем я прочитал строку, используя Marshall.PtrToStringAnsi(ptr). После того, как я получаю копию строки, я просто вызываю другой метод c HeapDestroy, который находится в библиотеке-оболочке c, которая вызывает free(ptr).

Вот вопрос; Недавно, пока он работает как часы, я начал получать исключение «Попытка чтения или записи в защищенную область памяти». После более глубокого анализа я понял, я верю, хотя я вызываю метод free для этого указателя, значение указателя не очищается, и это заполняет кучу без присмотра и заставляет мой рабочий процесс iis выдавать это исключение. Кстати, это проект веб-сайта, который вызывает этот метод в библиотеке c.

Не могли бы вы помочь мне в этом вопросе?

Конечно, вот код C#;

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    private extern static int ReturnString();

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    private extern static void HeapDestroy(int ptr);

    public static string GetString()
    {
        try
        {

            int i = ReturnString();
            string result = String.Empty;
            if (i > 0)
            {
                IntPtr ptr = new IntPtr(i);
                result = Marshal.PtrToStringAnsi(ptr);
                HeapDestroy(i);
            }

            return result;
        }
        catch (Exception e)
        {
            return String.Empty;
        }
    }

person Emrah GOZCU    schedule 30.09.2009    source источник
comment
Не могли бы вы опубликовать код С#, который вы используете?   -  person Ian Kemp    schedule 30.09.2009


Ответы (3)


В чем может быть проблема, так это в базовом коде C. Вы не добавляете терминатор NULL к строке, на которую опирается strcat (или не проверяете возврат NULL от malloc). В этом случае легко получить поврежденную память. Вы можете исправить это, выполнив следующие действия.

retval[0] = '\0';
strcat(retval, "SOMETEXT");

Также часть проблемы заключается в том, что вы обманываете систему. Гораздо лучше написать его правильно и дать системе работать на корректно функционирующем коде. Первым шагом является исправление собственного кода для правильного возврата строки. Одна вещь, которую вам нужно учитывать, это то, что CLR может освобождать только определенные типы памяти (выделения HGlobal и CoTask). Итак, давайте изменим сигнатуру функции, чтобы она возвращала char* и использовала другой распределитель.

_declspec(dllexport) char* ReturnString()
{
 char* retval = (char *) CoTaskMemAlloc(125);
 retval[0] = '\0';
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return retval;
}

Затем вы можете использовать следующую подпись C# и освободить IntPtr с помощью Marshal.FreeCoTaskMem.

[DllImport("SomeDll.dll")]
public static extern IntPtr ReturnString();

Хотя даже лучше. Если при маршаллинге CLR решит, что ей нужно освободить память, она будет использовать для этого FreeCoTaskMem. Обычно это относится к возврату строки. Поскольку вы выделили память с помощью CoTaskMemAlloc, вы можете сохранить себе этапы сортировки + освобождения и выполнить следующие действия.

[DllImport("SomeDll.dll", CharSet=Ansi)]
public static extern String ReturnString();
person JaredPar    schedule 30.09.2009
comment
Я попробую это! Кажется разумным. - person Emrah GOZCU; 30.09.2009
comment
JaredPar, твой ответ очень полезен. Но еще вопрос к вашему примеру, что эквивалентно CoTaskMemAlloc в ansi-c, есть ли такие в стандартной библиотеке? - person Emrah GOZCU; 30.09.2009
comment
@Emrah, в стандартной библиотеке ansi-c нет эквивалента. Если вы ограничены ansi-c, вам, вероятно, потребуется бесплатно написать быстрый слой PInvoke и передать туда IntPtr. - person JaredPar; 30.09.2009

Освобождение памяти не очищает ее, она просто освобождает ее, чтобы ее можно было использовать повторно. Некоторые отладочные сборки будут перезаписывать память, чтобы упростить поиск проблем с такими значениями, как 0xBAADFOOD.

Вызывающие должны выделять память, никогда не возвращать выделенную память:

_declspec(dllexport) int ReturnString(char*buffer, int bufferSize)
{
    if (bufferSize < 125) {
        return 125;
    } else {
        strcat(buffer, "SOMETEXT");
        strcat(buffer, "SOMETEXT MORE");
        return 0;
    }
}
person Tom    schedule 30.09.2009

Хотя память выделяется DLL в той же куче, что и ваше приложение, она МОЖЕТ использовать другой менеджер памяти, в зависимости от библиотеки, с которой она была связана. Вам нужно либо убедиться, что вы используете ту же самую библиотеку, либо добавить код для освобождения памяти, выделяемой DLL, в самом коде DLL.

person justinhj    schedule 30.09.2009
comment
Да, именно поэтому добавлен метод HeapDestroy, который содержит простой метод free(ptr) для вызова. - person Emrah GOZCU; 30.09.2009
comment
Прохладно. Я неправильно понял ваш вопрос. Я думал, что вы написали оболочку для DLL, поэтому DLL выполняла выделение памяти, в то время как ваш код оболочки выполнял свободные операции. - person justinhj; 30.09.2009
comment
Я бы рекомендовал использовать более безопасные операции, которые работают с ограниченными строками. Поместите свой размер буфера в константу и передайте его функциям, которые работают с выделенными строковыми данными. Используйте strncat и так далее. - person justinhj; 30.09.2009
comment
@justinhj, на самом деле мой образец представляет собой ограниченную строку, моя фактическая строковая переменная не ограничена. В любом случае, спасибо за советы. - person Emrah GOZCU; 30.09.2009