Как передать указатель функции из управляемого С++ (С++/CLI) в неуправляемый метод? Я прочитал несколько статей, таких как этот из MSDN, но он описывает две разные сборки, в то время как я хочу только один.
Я попытался создать мой управляемый делегат, а затем попытался использовать метод Marshal::GetFunctionPointerForDelegate
, но я не смог его скомпилировать.
Ответ 2
Вот еще один способ сделать это, основываясь на моем опыте реализации оболочки .NET в C++/CLI вокруг библиотеки рендеринга карт CartoType C++. Это проверенный и рабочий код.
API C++ имеет асинхронную функцию поиска, которая принимает обратный вызов:
TResult CartoType::CFramework::FindAsync(FindAsyncCallBack aCallBack,const TFindParam& aFindParam,bool aOverride = false);
Обратный вызов - это функция этого типа:
using FindAsyncCallBack = std::function<void(std::unique_ptr<CMapObjectArray> aMapObjectArray)>;
Задача - предоставить оболочку .NET для этой функции, добавив код C++/CLI в существующую систему оболочек. Сначала я определяю подходящий тип делегата для моей функции .NET (эквивалент FindAsyncCallback в API C++):
public delegate void FindAsyncDelegate(MapObjectList^ aMapObjectList);
Подпись функции .NET выглядит следующим образом:
Result FindAsync(FindAsyncDelegate^ aDelegate,FindParam^ aFindParam,bool aOverride);
Основная проблема реализации, которая должна быть решена, заключается в том, как вызвать собственную функцию C++ и предоставить собственную функцию обратного вызова, которая затем может вызвать делегат, переданный вызывающей стороной функции .NET. Связанная задача состоит в том, чтобы поддерживать делегат и собственный объект функции обратного вызова живыми, пока поток асинхронной функции не выполнит свою работу. Вот как это делается.
Я определяю тип делегата C++/CLI, который совпадает с типом функции обратного вызова C++, и класс для хранения делегата, передаваемого вызывающей стороной в функцию .NET (типа FindAsyncDelegate), и делегат тип для передачи в C++ (типа NativeAsyncHandler):
delegate void NativeAsyncHandler(std::unique_ptr<CMapObjectArray> aMapObjectArray);
ref class FindAsyncHelper
{
public:
FindAsyncHelper(Framework^ aFramework,FindAsyncDelegate^ aDelegate):
m_framework(aFramework),
m_delegate(aDelegate)
{
}
void Handler(std::unique_ptr<CMapObjectArray> aMapObjectArray)
{
MapObjectList^ o = gcnew MapObjectList;
SetMapObjectList(m_framework,o,*aMapObjectArray);
m_delegate(o);
// Remove this object from the list held by the framework so that it can be deleted.
m_framework->m_find_async_helper_list->Remove(this);
}
Framework^ m_framework;
FindAsyncDelegate^ m_delegate;
NativeAsyncHandler^ m_native_handler;
};
Идея состоит в том, что мы создаем объект FindAsyncHelper с двумя делегатами в нем, а затем вызываем собственную функцию FindAsync с собственным делегатом, организованным для вызова Handler(), который затем вызывает оригинальный делегат вызывающего абонента.
И вот как это реализовано:
typedef void(*FIND_ASYNC_CALLBACK)(std::unique_ptr<CMapObjectArray> aMapObjectArray);
Result Framework::FindAsync(FindAsyncDelegate^ aDelegate,FindParam^ aFindParam,bool aOverride)
{
if (aDelegate == nullptr || aFindParam == nullptr)
return Result::ErrorInvalidArgument;
TFindParam param;
SetFindParam(param,aFindParam);
FindAsyncHelper^ h = gcnew FindAsyncHelper(this,aDelegate);
h->m_native_handler = gcnew NativeAsyncHandler(h,&FindAsyncHelper::Handler);
IntPtr p = Marshal::GetFunctionPointerForDelegate(h->m_native_handler);
FIND_ASYNC_CALLBACK f = static_cast<FIND_ASYNC_CALLBACK>(p.ToPointer());
TResult error = m_framework->FindAsync(f,param,aOverride);
// Keep h alive by adding it to a list.
m_find_async_helper_list->Add(h);
return (Result)(int)error;
}
Некоторые заметки:
Заявления
FindAsyncHelper^ h = gcnew FindAsyncHelper(this,aDelegate);
h->m_native_handler = gcnew NativeAsyncHandler(h,&FindAsyncHelper::Handler);
создать объект FindAsyncHandler и сохранить в нем собственный объект-обработчик; сохранение его здесь означает, что у нас есть только один объект для сохранения, FindAsyncHandler. Следующие утверждения:
IntPtr p = Marshal::GetFunctionPointerForDelegate(h->m_native_handler);
FIND_ASYNC_CALLBACK f = static_cast<FIND_ASYNC_CALLBACK>(p.ToPointer());
получить указатель на функцию, который можно передать в собственный код, и привести его к нужному типу указателя на функцию. Мы не можем привести его напрямую к типу std::function, используемому в FindAsyncCallback, поэтому необходим громоздкий дополнительный typedef.
Наконец, можно вызвать встроенную функцию FindAsync:
TResult error = m_framework->FindAsync(f,param,aOverride);
А затем, чтобы убедиться, что различные функции обратного вызова остаются в живых, FindAsyncHandler добавляется в список, принадлежащий основному объекту фреймворка:
m_find_async_helper_list->Add(h);
Он удаляется из списка, когда задача выполнена и вызывается FindAsyncHelper :: Handler.