Обертывание неуправляемого c ++ в управляемой оболочке - PullRequest
1 голос
/ 22 января 2009

У меня есть неуправляемая библиотека C ++. Я хотел бы раскрыть функциональность для приложений .NET. Есть одна особенность, которую я не знаю, как справиться:

typedef void (free_fn *) (void *); void put (void * data, free_fn deallocation_function);

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

void * p = malloc (100); ... заполнить буфер ... положить (р, бесплатно);

Как я могу предоставлять такие вещи приложениям .NET?

Ответы [ 7 ]

5 голосов
/ 23 января 2009

Будьте очень осторожны, когда делаете это. На самом деле .NET действительно хочет, чтобы его объекты были закреплены на пути к неуправляемой рутине и откреплены на выходе. Если ваш неуправляемый код содержит значение указателя, которое было закреплено по пути, тогда существует реальная вероятность того, что память будет перемещена, или сборщик мусора, или оба.

Это особенно относится к делегатам, назначенным для работы с указателями (поверьте мне - я обнаружил, что маршалые делегаты собирали для меня мусор - у меня в Microsoft люди это проверяли). Окончательное решение этой проблемы - спрятать копии ваших делегатов в статической таблице в паре с уникальным идентификатором транзакции, а затем создать неуправляемую функцию, которая при вызове ищет делегата в таблице через идентификатор транзакции, а затем выполняет ее. Это некрасиво, и если бы у меня был другой выбор, я бы этим воспользовался.

Вот лучший способ сделать это в вашем случае - так как ваш неуправляемый код использует настройку it it и забудет модель, то вы должны сделать API более коротким. Создайте оболочку в управляемом C ++, которая выделяет память с помощью неуправляемой подпрограммы, копирует в нее ваши данные и затем передает их вместе с указателем на неуправляемый освобождающий модуль.

2 голосов
/ 23 января 2009

Как правило, пользователи .NET вашей библиотеки не будут передавать динамически созданные массивы вашим функциям. Насколько я знаю, все контейнеры в .NET являются сборщиком мусора.

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

При написании оболочек .NET для неизмененного кода я обнаружил, что вы хотите больше сосредоточиться на сохранении функциональности, чем на том, чтобы сделать каждую функцию доступной в .NET. В вашем примере может быть лучше, чтобы управляемая оболочка скопировала массив в неуправляемую память и выполняла все необходимые операции внутри библиотеки. Таким образом, вам не нужно делать какое-либо закрепление управляемой памяти или сортировку управляемой памяти на неуправляемую память, чтобы обойти сборку мусора во время выполнения .NET. Однако то, как вы реализуете управляемую оболочку, действительно зависит от цели этой функции.

Если вы действительно хотите реализовать эту функцию для функции в .NET, вам нужно взглянуть на класс Marshal в .NET для получения контроля над управляемой памятью в неуправляемом коде.

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

1 голос
/ 21 февраля 2009

Некоторые из предыдущих авторов использовали MC ++, что устарело. C ++ / CLI является гораздо более элегантным решением.

BEST, метод взаимодействия, это неявное взаимодействие, а не явное. Я не верю, что кто-нибудь еще это прокомментировал. Однако это дает вам возможность маршалировать ваши типы из управляемого <-> native, где, если вы внесете изменение в определение типа или структуру структуры, это не приведет к критическому изменению (что делает явное взаимодействие).

Эта статья в википедии описывает некоторые различия и является хорошей отправной точкой для получения дополнительной информации.

P / Invoke (явный и неявный)

Кроме того, на сайте marshal-as.net есть некоторые примеры и информация относительно этого более нового метода (опять же, более идеальный, поскольку он не нарушит ваш код, если родная структура будет переопределена) .

1 голос
/ 23 января 2009

В большинстве ответов предлагается копировать данные из управляемого буфера в неуправляемый буфер. Как именно вы это сделаете? Является ли следующая реализация в порядке?

void managed_put (byte data_ __gc[], size_t size_)
{
    //  Pin the data
    byte __pin *tmp_data = &data_[0];

    //  Copy data to the unmanaged buffer.
    void *data = malloc (size_);
    memcpy (data, (byte*) tmp_data, size_);

    //  Forward the call
    put (data, size_, free);
}
1 голос
/ 23 января 2009

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

Если у вас были смелости (и мазохистская выносливость), вы могли бы закрепить буфер в оболочке, а затем передать маршалированный делегат управляемой функции, которая открепляет буфер. Однако я бы не стал это предлагать. Необходимость сделать пару управляемых упаковщиков научила меня раскрывать абсолютную минимальную неуправляемую функциональность, даже если это означает, что вам нужно переписать некоторые вещи в управляемом коде. Пересечь эту границу почти так же легко, как раньше, если не сказать о хитах производительности.

0 голосов
/ 23 января 2009

Поскольку вы упомянули, что это было асинхронно, я бы сделал это так. Открытая функция .Net только принимает данные, но не принимает делегата. Ваш код передает закрепленные данные и указатель на функцию, которая просто открепит данные. Это оставляет очистку памяти для GC, но гарантирует, что она не очистит ее, пока не будет выполнена асинхронная часть.

0 голосов
/ 22 января 2009

Вы должны иметь управляемые оболочки для самих функций (или неуправляемые оболочки, если хотите передать управляемые функции). Или же, обрабатывайте указатели неуправляемых функций как непрозрачные маркеры в управляемом мире.

...