Большинство современных архитектур будут иметь некоторую инструкцию для поиска положения младшего бита набора или самого старшего бита или подсчета числа ведущих нулей и т. д.
Если у вас есть одна инструкция этого
Потратьте немного времени на то, чтобы работать над ним на бумаге, и понимаете, что x & (x-1) очистит младший бит набора в x, а ( x & ~(x-1) ) вернет только самый младший бит набора , независимо от архитектуры, длины слова и т. д. Зная это, тривиально использовать аппаратное число count-leading-zeroes / maximum-set-bit для поиска младшего бита набора, если нет явной инструкции для этого.
Если не существует соответствующей аппаратной поддержки вообще, реализованная здесь многократная и нулевая реализация приведенных здесь отсчетов количества отсчетов или одна из них на странице бит Twiddling Hacks можно тривиально преобразовать, чтобы дать минимальный бит бит, используя вышеприведенную идентичности и имеет то преимущество, что он является бесконтактным.
Управляемая строка не совпадает с символом char *. Что происходит под прикрытием, так это то, что код маршалинга на слое interop делает копию неуправляемой строки, чтобы преобразовать ее в управляемую строку, но она не может освободить эту память, поскольку она не знает, как она была выделена.
Однако вы можете попробовать выделить и вернуть BSTR вместо char *.
Причина, по которой это имеет значение, - это то, как выделяются char * и BSTR в памяти.
Символ * буферы распределяются в куче среды выполнения C ++ с использованием частных процедур распределения / освобождения, которые CLR ничего не знает, поэтому нет возможности удалить эту память. И чтобы еще хуже, буфер, который char * указывает, может быть выделен внутренней реализацией кучи DLL-кода или даже может указывать на переменную-член в частном классе.
BSTR on другая сторона выделяется с использованием WIndows API SysAllocString и освобождается SyFreeStirng, и поскольку уровень взаимодействия CLR об этих API Windows знает, как освободить BSTR от неуправляемого кода.
Вы не можете освобождать неуправляемую память от управляемого кода. Вам нужно написать подпрограмму в C, которая называет free
указателем, возвращаемым функцией Run
, и P / вызывается из .NET.
Another опция состоит в том, чтобы выделить неуправляемую память в .NET, передать указатель на функцию C, которая заполнит ее данными и, наконец, освободит этот указатель:
IntPtr ptr = Marshal.AllocHGlobal (100 * sizeof ( символ)); SomeUnmanagedFunc (PTR); Marshal.FreeHGlobal (PTR);
Другой способ сделать это - передать управляемую строку (экземпляр 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); Выполнить (результат);
Маршрутизатор 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 ();
Я читал некоторые вопросы о 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; }