如何将函数指针从托管C ++(C ++ / CLI)传递给非托管方法?我读了一些文章,比如this one from MSDN,但它描述了两个不同的集合,而我只想要一个。
这是我的代码:
1)标题(MyInterop.ManagedCppLib.h):
#pragma once
using namespace System;
namespace MyInterop { namespace ManagedCppLib {
public ref class MyManagedClass
{
public:
void DoSomething();
};
}}
2)CPP代码(MyInterop.ManagedCppLib.cpp)
#include "stdafx.h"
#include "MyInterop.ManagedCppLib.h"
#pragma unmanaged
void UnmanagedMethod(int a, int b, void (*sum)(const int))
{
int result = a + b;
sum(result);
}
#pragma managed
void MyInterop::ManagedCppLib::MyManagedClass::DoSomething()
{
System::Console::WriteLine("hello from managed C++");
UnmanagedMethod(3, 7, /* ANY IDEA??? */);
}
我尝试创建托管代理,然后尝试使用Marshal::GetFunctionPointerForDelegate
方法,但我无法编译。
答案 0 :(得分:42)
是的,你想要Marshal :: GetFunctionPointerForDelegate()。您的代码段缺少您想要调用的托管函数,我只是编写了一个。您还必须声明托管委托类型并在获取函数指针之前创建它的实例。这很有效:
#include "stdafx.h"
using namespace System;
using namespace System::Runtime::InteropServices;
#pragma managed(push, off)
typedef void (* UnmanagedSummer)(int arg);
void UnmanagedMethod(int a, int b, UnmanagedSummer sum)
{
int result = a + b;
sum(result);
}
#pragma managed(pop)
ref class Test {
delegate void ManagedSummer(int arg);
public:
static void Run() {
Test^ t = gcnew Test();
ManagedSummer^ managed = gcnew ManagedSummer(t, &Sum);
IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(managed);
UnmanagedSummer functionPointer = static_cast<UnmanagedSummer>(stubPointer.ToPointer());
UnmanagedMethod(1, 2, functionPointer);
GC::KeepAlive(managed); // Important: ensure stub can't be collected while native code is running
System::Diagnostics::Debug::Assert(t->summed == 3);
}
void Sum(int arg) {
summed += arg;
}
int summed;
};
int main(array<System::String ^> ^args)
{
Test::Run();
return 0;
}
答案 1 :(得分:0)
根据我的经验,这是围绕CartoType C ++地图呈现库在C ++ / CLI中实现.NET包装器的另一种方法。这是经过测试的有效代码。
C ++ API具有异步的Find函数,该函数接受回调:
TResult CartoType::CFramework::FindAsync(FindAsyncCallBack aCallBack,const TFindParam& aFindParam,bool aOverride = false);
回调是这种类型的函数:
using FindAsyncCallBack = std::function<void(std::unique_ptr<CMapObjectArray> aMapObjectArray)>;
任务是通过向现有包装系统中添加C ++ / CLI代码来为此功能提供.NET包装。首先,我为.NET函数定义一个合适的委托类型(等效于C ++ API中的FindAsyncCallback):
public delegate void FindAsyncDelegate(MapObjectList^ aMapObjectList);
.NET函数的签名为:
Result FindAsync(FindAsyncDelegate^ aDelegate,FindParam^ aFindParam,bool aOverride);
要解决的主要实现问题是如何调用本机C ++函数并提供本机回调函数,该回调函数随后可以调用.NET函数的调用者传递的委托。关联的任务是使委托和本机回调函数对象保持活动状态,直到异步函数的线程完成其工作为止。就是这样。
我定义了一个与C ++回调函数类型相同的C ++ / CLI委托类型,以及一个用于保存调用方传递给.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());
获取可以传递给本机代码的函数指针,并将其强制转换为正确的函数指针类型。我们不能将其直接转换为FindAsyncCallback中使用的std :: function类型,因此需要繁琐的额外typedef。
最后可以调用本机的FindAsync函数:
TResult error = m_framework->FindAsync(f,param,aOverride);
然后,为确保各种回调函数保持活动状态,将FindAsyncHandler添加到主框架对象拥有的列表中:
m_find_async_helper_list->Add(h);
任务完成并调用FindAsyncHelper :: Handler时,该列表将从列表中删除。