Может ли свободная память Marshal.FreeHGlobal выделяться из неуправляемого кода? [dубликат]

Большинство современных архитектур будут иметь некоторую инструкцию для поиска положения младшего бита набора или самого старшего бита или подсчета числа ведущих нулей и т. д.

Если у вас есть одна инструкция этого

Потратьте немного времени на то, чтобы работать над ним на бумаге, и понимаете, что x & (x-1) очистит младший бит набора в x, а ( x & ~(x-1) ) вернет только самый младший бит набора , независимо от архитектуры, длины слова и т. д. Зная это, тривиально использовать аппаратное число count-leading-zeroes / maximum-set-bit для поиска младшего бита набора, если нет явной инструкции для этого.

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

6
задан Alex 19 December 2009 в 09:33
поделиться

6 ответов

Управляемая строка не совпадает с символом char *. Что происходит под прикрытием, так это то, что код маршалинга на слое interop делает копию неуправляемой строки, чтобы преобразовать ее в управляемую строку, но она не может освободить эту память, поскольку она не знает, как она была выделена.

Однако вы можете попробовать выделить и вернуть BSTR вместо char *.

Причина, по которой это имеет значение, - это то, как выделяются char * и BSTR в памяти.

Символ * буферы распределяются в куче среды выполнения C ++ с использованием частных процедур распределения / освобождения, которые CLR ничего не знает, поэтому нет возможности удалить эту память. И чтобы еще хуже, буфер, который char * указывает, может быть выделен внутренней реализацией кучи DLL-кода или даже может указывать на переменную-член в частном классе.

BSTR on другая сторона выделяется с использованием WIndows API SysAllocString и освобождается SyFreeStirng, и поскольку уровень взаимодействия CLR об этих API Windows знает, как освободить BSTR от неуправляемого кода.

6
ответ дан Franci Penov 16 August 2018 в 03:46
поделиться
-1
ответ дан Andrew Keith 16 August 2018 в 03:46
поделиться

Вы не можете освобождать неуправляемую память от управляемого кода. Вам нужно написать подпрограмму в C, которая называет free указателем, возвращаемым функцией Run , и P / вызывается из .NET.

Another опция состоит в том, чтобы выделить неуправляемую память в .NET, передать указатель на функцию C, которая заполнит ее данными и, наконец, освободит этот указатель:

  IntPtr ptr = Marshal.AllocHGlobal (100 * sizeof (  символ));  SomeUnmanagedFunc (PTR);  Marshal.FreeHGlobal (PTR);   
6
ответ дан Darin Dimitrov 16 August 2018 в 03:46
поделиться
  • 1
    Не могли бы вы представить простой пример, пожалуйста. – Alex 19 December 2009 в 09:39
  • 2
    Распределение памяти в приложении .NET предполагает, что мы знаем размер массива заранее. В реальном решении (проблема одна) это полная тайна :). – Alex 19 December 2009 в 09:58
  • 3
    @Darin - Как вы думаете, будет ли первый метод работать надежно? Есть ли какие-либо трюки или что-то, что должно быть реализовано. Должен ли я переносить вызов неуправляемой свободной памяти в метод IDisposable для моего класса, который обрабатывает неуправляемую память? – Eugeniu Torica 24 February 2012 в 23:16

Другой способ сделать это - передать управляемую строку (экземпляр StringBuilder) через P / Invoke (в качестве параметра вашей функции Run ).

Таким образом никакие выделения не выполняются на неуправляемой стороне.

Другими словами, у вас будет что-то вроде:

  extern «C» __declspec (dllexport) void __cdecl Run (char *  data) {// заполнить массив данными // no return value (void)}  

и вызвать его следующим образом:

  [DllImport ("  Unmanaged.dll ", CharSet = CharSet.Ansi)] static extern void Run (результат StringBuilder);  Результат StringBuilder = новый StringBuilder (100);  Выполнить (результат);   
3
ответ дан Groo 16 August 2018 в 03:46
поделиться
  • 1
    Как ответ Дарина, он будет работать, если известен размер необходимой памяти. – Eugeniu Torica 24 February 2012 в 23:10

Маршрутизатор P / Invoke предположит, что память для типа возврата была выделена CoTaskMemAlloc () и вызовет CoTaskMemFree (), чтобы освободить его. Если это не было сделано, программа потерпит неудачу с исключением в Vista и Win7, но без проблем протекает память на XP. Использование SysAllocString () может быть выполнено для работы, но вы должны аннотировать тип возвращаемого значения в атрибуте [DllImport]. Не делать этого все равно вызовет утечку, без диагностики на Win7. BSTR является not указателем на блок памяти, выделенный CoTaskMemAlloc, перед указанным адресом есть 4 байта, которые хранят размер строки.

Любой из следующих комбинации будут работать правильно:

  extern «C» __declspec (dllexport) BSTR __stdcall ReturnsAString () {return SysAllocString (L «Hello world»);  } [DllImport (@ "c: \ projects \ cpptemp1 \ debug \ cpptemp1.dll")] [return: MarshalAs (UnmanagedType.BStr)] // ПРИМЕЧАНИЕ: требуется!  частная статическая внешняя строка ReturnsAString ();   

Или:

  extern «C» __declspec (dllexport) const wchar_t * __stdcall ReturnsAString () {const wchar_t * str = L «Hello world»;  wchar_t * retval = (wchar_t *) CoTaskMemAlloc ((wcslen (str) +1) * sizeof (wchar_t));  wcscpy (retval, str);  return retval;  } [DllImport (@ "c: \ projects \ cpptemp1 \ debug \ cpptemp1.dll", CharSet = CharSet.Auto)] частная статическая внешняя строка ReturnsAString ();   

Вы должны подумать о том, чтобы позволить клиентскому коду передать буфер, чтобы не было проблем с управлением памятью. Это должно выглядеть так:

  extern «C» __declspec (dllexport) void __stdcall ReturnsAString (wchar_t * buffer, size_t buflen) {wcscpy_s (буфер, buflen, L "Hello world")  ;  } [DllImport (@ "c: \ projects \ cpptemp1 \ debug \ cpptemp1.dll", CharSet = CharSet.Auto)] private static extern void ReturnsAString (буфер StringBuilder, int buflen);  ... StringBuilder sb = new StringBuilder (256);  ReturnsAString (sb, sb.Capacity);  string s = sb.ToString ();   
7
ответ дан Hans Passant 16 August 2018 в 03:46
поделиться

Я читал некоторые вопросы о PInvoke, и я остановился здесь. Я не знаю, действительно ли проблема по-прежнему актуальна для вас, но я решил опубликовать свой ответ будущим читателям.

Это ваш последний комментарий к ответу Дарина Димитрова. Когда размер выделенной памяти неизвестен, типичным решением является вызов неуправляемой функции с нулевым указателем и получение размера в выходном параметре. Затем мы выделяем необходимое пространство и снова вызываем неуправляемую функцию.

Пример ниже:

  // MANAGED SIDE IntPtr ptr = IntPtr.Zero;  int size = 0;  myFunc (ptr, размер);  ptr = Marshal.AllocHGlobal (размер);  myFunc (ptr, размер);  // Делаем вашу работу .. Marshal.FreeHGlobal (ptr);  // UNMANEGED SIDE int myFunc (void * dest, size_t & amp; size) {if (dest == NULL) // вычисляет размер. Size = 'resul' return 0;  } // создаем массив и копируем все элементы memcopy (dest, ..., size);  // освободить все выделенное пространство в неуправляемом и возврате успешного возврата 0;  }  
2
ответ дан Zé Carlos 16 August 2018 в 03:46
поделиться
  • 1
    Весьма интересный подход, но он предполагает, что время выполнения управляемого кода очень быстро, поэтому мы можем назвать его дважды. На практике управляемый код может: а) выполнять медленно; b) возвращать несколько разные результирующие наборы, поэтому выделенной памяти будет недостаточно – Eugeniu Torica 24 February 2012 в 23:13
Другие вопросы по тегам:

Похожие вопросы: