Search code examples
delegatesc++-cliinteropfunction-pointers

c++/cli pass (managed) delegate to unmanaged code


How do I pass a function pointer from managed C++ (C++/CLI) to an unmanaged method? I read a few articles, like this one from MSDN, but it describes two different assemblies, while I want only one.

Here is my code:

1) Header (MyInterop.ManagedCppLib.h):

#pragma once

using namespace System;

namespace MyInterop { namespace ManagedCppLib {

    public ref class MyManagedClass
    {
    public:
        void DoSomething();
    };
}}

2) CPP Code (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??? */);
}

I tried creating my managed delegate and then I tried to use Marshal::GetFunctionPointerForDelegate method, but I couldn't compile.


Solution

  • Yes, you want Marshal::GetFunctionPointerForDelegate(). Your code snippet is missing the managed function you'd want to call, I just made one up. You will also have to declare the managed delegate type and create an instance of it before you can get a function pointer. This worked well:

    #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;
    }