Click here to Skip to main content
Click here to Skip to main content

Convert a Delegate to a Function pointer to Implement Callback function

By , 2 Feb 2003
 

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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

kero
Web Developer
United States United States
Member
A programer
Born in China
work in Japan
25 years old
Male

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionMarshal directive exception was unhandledmemberA.Gokulakannan25 Aug '09 - 17:42 
hi all,
 
i just done a DLL/CLR file using c++. i tested the file on MFC application and its worked well.
but i have problem when test it in a vb windows application.
 
this is the vb code;
 
Imports System
Imports System.Runtime.InteropServices
 
Public Class Form1
 
Private Declare Auto Function AddNumbers Lib "New_CLR_DLL.DLL" ( _
<MarshalAsAttribute(UnmanagedType.AsAny)> ByVal a As Integer, <MarshalAsAttribute(UnmanagedType.AsAny)> ByVal b As Integer)
 
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 
Dim result As Int32
 
result = AddNumbers(3, 5)
MsgBox(result)
 

End Sub
End Class
 

>> the program compiles but when run it, it shows an error (PInvoke restriction: cannot return variants.) near result = AddNumbers (3, 5)
showing marshalDirectiveException was unhandled.
 
any help will be deeply appreciated..
thanks.
 
Best Regards,
Gokulakannan.
QuestionMarshal.GetFunctionPointerForDelegate?memberblue2v10 Apr '09 - 9:29 
Why didn't you use this built-in method?
AnswerRe: Marshal.GetFunctionPointerForDelegate?memberZhixiang Zhu15 May '12 - 22:16 
TRUE! This one is much simpler! Maybe this API did not exist by the time the author wrote this article?
General!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Don´t use this code !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!memberElmue22 Apr '08 - 16:52 
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Don´t use this code !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
Your code works at the first look.
But if you put it into a bigger project you will notice that it fails very ugly.
 
Your example code only works because you execute the Callback IMMEDIATELY after calling Marshal::StructureToPtr().
But if you let pass some time between calling StructureToPtr() and executing the callback, the application is aborted without any usefull error messasge - a SILENT CRASH.
 
The reason is that the framework moves objects around in memory and a delegate's address in memory is only valid for a while and then it becomes invalid.
 
The very strange thing is the way Visual Studio (2003) behaves:
The callbacks functioned in my project when starting the application directly from Visual Studio with the key F5.
But if I started the same application hitting CTRL + F5 it failed!!
 
THERE IS ABSOLUTELY NO WAY PASSING A MANAGED CALLBACK TO UNMANAGED CODE AS IF IT WAS A REAL C++ CALLBACK.
 
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Don´t use this code !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
There are ONLY few reliable ways to call callbacks from unmanaged C++ code :
 
1.)
Call a STATIC managed function directly from unmanaged C++ code without using any delegate.
 
2.)
Create a GCHandle from the delegate and pass the GCHandle to unmanaged code instead of a function pointer.
 
3.)
If you want something that behaves like a real callback, it becomes more complicated:
Inside your managed class (__gc), which you want to be called back from unmanaged code you have to put an inner unmanaged (__nogc) class.
 
If you want to see a working code which uses a callback from unmanaged C++ code into a managed class, download the latest version of my CabLib.dll.
There study the file LibExtract.h and you see how to do it the correct way!
 
You find the latest version of CabLib here
 
ATTENTION:
You need CabLib version 9.0 from April 2008, in older versions you will not find these callbacks!
 
----------------------------------
 
I gave your artivle the worst note because you did a poor testing and I spent an entire weekend finding out the reason and a solution. It seems that you did not understand what Nish wrote in his article.
 
I asked Codeproject to delete your article as it misleads programmers around the world to waste their time with this code which simply does not work!
 
Elmü
Questionhello world !memberbibiboule2 Jan '07 - 21:33 

This example is very close about my trouble.
 
I have a function pointer like this :
 
typedef bool (*name)(void * parameter);
 
And i have a system call like this :
 
API(name, param);
 
When i invoke the API, i dont have error but when i have my function in a class, the compiler
ask me to define parameter ...
 
thx all of yours reply Smile | :)
QuestionWhat about an ArrayList instead an Int????memberfranjie27 May '05 - 3:45 
Hi Big Grin | :-D ! This is what I was looking for but, I need to send and received an ArrayList instead just an int. The thing is that when I change the types in the delegate like this:
 
__delegate void MyDelegate(System::Collections::ArrayList* nArg);
 
I get eventually this error: Frown | :(
 
Additional information: Can not marshal parameter #1: The type definition of this type has no layout information.
 
Should I do differently when using ArrayList instead int??
 
Thanks Wink | ;)
 
******************
 
There are 10 types of persons, those who understand binayr numbers, and those who doesn't.
 
******************
Generalmanaged class as member variable in unmanaged classmemberbaiju.km17 Aug '04 - 3:30 
hi,
any body know how to make managed class as member variable in unmanaged class.
ie
unmaged class
{
managed *p managed;
}
 
by
baijumax
GeneralSuccessive invocations failsusshnipak16 Sep '03 - 3:14 
Hi,
 
I tried your approach, but for me only first callback invocation work. Next time the unmanaged machinery is about to invoke the callback, it fails with "NullReferenceException"...
 
Any idea what could be wrong? Thank you very much!
 
Ales Pour
GeneralRe: Successive invocations failmembernutty11 Jul '04 - 20:55 
Hi there,
 
as far as I know, the garbage collector may move managed structs to other location in memory undeterministically. The Pin function should be used to prevent it from doing so.
 
Also I thougth until a few days ago, that unmanaged structs canot holt references to managed types. This is not true however. Use the gcroot.
It goes like this:
 

#include
 
gcroot _object;
 
This _object needs not to be pinned, because the gcroot pointer is registered with the .net framework. If the object is relocated, the pointer gets updated to the new position.
 
Ingo

GeneralRe: Declaring Managed class as Member variable in an unmanaged classmemberbaiju.km24 Aug '04 - 1:52 
hi ,
finally i found the solution.
 
add #include

gcroot m_oManaged;

u have to handle garbage collection explicitly.
 


 
by
baijumaxBlush | :O

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 3 Feb 2003
Article Copyright 2003 by kero
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid