65.9K
CodeProject is changing. Read more.
Home

Convert a Delegate to a Function pointer to Implement Callback function

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (13 votes)

Feb 3, 2003

2 min read

viewsIcon

153533

downloadIcon

978

Convert a delegate to a function pointer to implement callback function, for mixed Managed C++ and unmanaged C++ coding, and for DLL call.

Introduction

These days, I've read the article Implementing Callback functions using IJW (avoiding DllImport), by Nishant S. From which I benefited much. And I wonder if there is some way to convert a delegate to a function pointer, I've got it at last.

When you are calling a function which is in a DLL, and that function requires a callback function pointer. First you can use DllImport (if you like to do so), which is a simple solution. But I don’t like DllImport. What's more, some times the function I want to call is not in a DLL. That is if I am coding mixed Managed C++ and Unmanaged C++. Even DllImport will not work. There are several ways to call a Managed C++ function in unmanaged C++ code.

  1. Use static function
     //managed class
    __gc class classA1 {
        public: static void func1(int nArg)    {
                     Console::WriteLine(nArg.ToString());
                }
    };
    //unmanaged class
    class classB1 {
        public: classB1(int nArg)    {
            m_nCount = nArg;
        }
        public: void func1()  {
            classA1::func1(m_nCount);
        }
        protected: int m_nCount;
    };

    When I need a non-static function to be called, there is the second solution.

  2. Use a managed object as a parameter.
     //managed class
    __gc class classA1 {
        public : void func2(int nArg)  {
                     Console::WriteLine(nArg.ToString());
                 }
    };
    //unmanaged class
    class classB1 {
        public: classB1(int nArg) {
            m_nCount = nArg;
        }
        public : void func2(classA1* pClassA1)     {
             pClassA1->func2(m_nCount);
         }
        protected: int m_nCount;
    };

    It seems to work well, but when the one which call pClassA1->func2(…) is not the function classB1:: func2(…)]. Maybe classB1:: func2(…) calls other object’s member function, and the function calls others, at last pClassA1->func2(…) is called sadly. You must hold pClassA1 as a parameter to run for a long way, blessing there is no virtual function. Virtual functions can’t have managed object as parameter. And an unmanaged object can’t have a member variable to hold managed object.

  3. Make a function pointer which points to an instance member function.

    I have not found any method in .NET framework SDK to convert a delegate to a function pointer straightly yet, but I noticed Marshal::StructToPtr() which announced to be able to convert a managed struct to unmanaged memory block. What it does is to wrap the delegate in the managed struct, then convert the struct to an unmanaged block. At the beginning I used an unmanaged struct that has a field of a function pointer act as the unmanaged memory block, like this:

    typedef void (*PFUNC)(int)
    
    Struct PFUNC_Wrapper
    {
        PFUNC thepfunc;
    };

    Use the managed struct like this:

    __delegate void MyDelegate(int nArg);
    [StructLayoutAttribute( LayoutKind::Sequential, 
                           CharSet = CharSet::Ansi )]
    __gc struct Delegate_Wrapper
    {
        [MarshalAsAttribute(UnmanagedType::FunctionPtr)]
        MyDelegate*  _theDelegate;
    };

    Then I convert the managed struct to an unmanaged struct. At the field of PFUNC thepfunc I got the function pointer. Ooh I got it! But soon I found I could also convert the unmanaged struct to a function pointer instead of accessing the member field of PFUNC thepfunc. Like this: *(PFUNC*)(&theUnmanagedStruct). Here you must have seen that the unmanaged struct is useless. So I just use a PFUNC to receive the data as an unmanaged memory block.

The source code

 __gc class classA1 {    
    public : void func2(int nArg) {
                 Console::WriteLine(nArg.ToString());
             }
};
typedef void (*PFUNC)(int);

class classB1 {
    public: classB1(int nArg) {
        m_nCount = nArg;
    }
    public: virtual void func3(PFUNC pCallback){
        if(pCallback != NULL)    {
            pCallback(m_nCount);
        }
    }
    protected: int m_nCount;
};
__delegate void MyDelegate(int nArg);

[StructLayoutAttribute( LayoutKind::Sequential, CharSet = CharSet::Ansi )]
__gc struct Delegate_Wrapper
{
    [MarshalAsAttribute(UnmanagedType::FunctionPtr)]
    MyDelegate*  _theDelegate;
};

// This is the entry point for this application
int _tmain(void)
{
    // TODO: Please replace the sample code below with your own.
    Console::WriteLine(S"Hello World");

    //create instance of managed classA1
    classA1* theA1 = new classA1();

    //create instance of Delegate_Wrapper
    Delegate_Wrapper* theWrapper = new Delegate_Wrapper();
    theWrapper->_theDelegate = new MyDelegate(theA1,&classA1::func2);
    
    //Convert the Delegate to Function Pointer.
    PFUNC pFnc;
    Marshal::StructureToPtr(theWrapper,&pFnc,false);
    
    //create instance of unmaged classB1
    classB1 theB1(3);

    //use the function pointer as a callback function
    theB1.func3(pFnc);
    
    Console::ReadLine();
    return 0;
}

Ensure that the garbage collector does not reclaim the delegate before the callback function completes its work.

Conclusion

A managed class’s member function can be used through a function pointer. Even if it is a non-static function!!

E-mail:peiweny@hotmail.com